For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navigation principale
Theme

Hooks

Exécutez des scripts ou des outils MCP pendant le cycle de vie de Codex

Les hooks permettent d’étendre les fonctionnalités de Codex. Ils exécutent des scripts ou des outils MCP à différentes étapes de la boucle de l’agent. Vous pouvez ainsi :

  • Envoyer la discussion à votre propre système de journalisation ou d’analyse
  • Analyser les prompts de votre équipe pour bloquer l’envoi de clés API collées par erreur
  • Résumer les discussions pour créer automatiquement des mémoires persistantes
  • Exécuter un contrôle personnalisé à la fin d’un tour de discussion pour faire respecter vos normes
  • Personnaliser les prompts lorsque vous travaillez dans un répertoire donné

Règles d’exécution des hooks :

  • Tous les hooks qui correspondent à l’événement s’exécutent, même s’ils proviennent de fichiers différents.
  • Si plusieurs hooks de commande correspondent au même événement, Codex les lance en parallèle. Aucun ne peut donc empêcher un autre hook correspondant de démarrer.
  • Avant qu’un hook non géré puisse s’exécuter, vous devez l’examiner et le déclarer fiable.

Les hooks s’exécutent à différents moments d’une conversation :

Moment Hooks
Pendant un tour PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop
À chaque tentative de requête au modèle PreModelRequest, PostModelRequest
Lorsque vous interrompez un tour en cours Interrupt (ne s’exécute pas pour les sous-agents)
Au démarrage d’une session ou d’un sous-agent SessionStart, SubagentStart
À la fermeture du fil principal SessionEnd (ne s’exécute pas pour les sous-agents)

Où Codex recherche les hooks

Pour chaque couche de configuration active, Codex recherche les hooks sous deux formes :

  • hooks.json
  • Des tables [hooks] définies directement dans config.toml

Les plugins installés peuvent également fournir une configuration du cycle de vie dans leur manifeste ou dans le fichier par défaut hooks/hooks.json. Consultez les règles d’empaquetage dans Créer des plugins.

En pratique, les quatre emplacements les plus utiles sont :

  • ~/.codex/hooks.json
  • ~/.codex/config.toml
  • <repo>/.codex/hooks.json
  • <repo>/.codex/config.toml

Si plusieurs sources définissent des hooks, Codex charge tous les hooks correspondants. Les couches de configuration prioritaires ne remplacent pas les hooks des couches de priorité inférieure. Si une même couche contient à la fois un fichier hooks.json et des tables [hooks] intégrées, Codex fusionne leurs définitions et affiche un avertissement au démarrage. Privilégiez une seule forme par couche.

Codex recherche également les hooks fournis par les plugins activés. Il les charge avec les hooks des autres sources et leur applique la même procédure de vérification qu’aux autres hooks non gérés.

Codex charge les hooks propres au projet uniquement si la couche .codex/ du projet est déclarée fiable. Dans les projets qui n’ont pas été déclarés fiables, Codex charge tout de même les hooks de l’utilisateur et du système depuis leurs couches de configuration actives respectives.

Examiner les hooks et les déclarer fiables

Codex répertorie les hooks configurés avant de déterminer lesquels peuvent s’exécuter. Pour qu’un hook non géré puisse s’exécuter, vous devez examiner sa définition exacte et la déclarer fiable. Codex associe cette décision à l’empreinte actuelle du hook. Il signale donc les hooks nouveaux ou modifiés et ne les exécute pas tant que vous ne les avez pas déclarés fiables.

Utilisez /hooks dans la CLI pour inspecter les sources, examiner les hooks nouveaux ou modifiés, les déclarer fiables ou désactiver individuellement des hooks non gérés. Si des hooks nécessitent un examen au démarrage, Codex affiche un avertissement vous invitant à ouvrir /hooks.

Les hooks gérés provenant du système, du MDM, du cloud ou de requirements.toml sont signalés comme gérés et déclarés fiables par la politique en vigueur. L’interface de consultation des hooks ne permet pas aux utilisateurs de les désactiver.

Pour une automatisation ponctuelle qui vérifie déjà les sources des hooks en dehors de Codex, passez --dangerously-bypass-hook-trust pour exécuter les hooks activés sans exiger de décision de confiance enregistrée pour cette invocation.

Structure de la configuration

Les hooks s’organisent sur trois niveaux :

  • Un événement, par exemple PreToolUse, PostToolUse, PreCompact, SubagentStart ou Stop
  • Un groupe qui définit le critère de correspondance pour cet événement
  • Un ou plusieurs gestionnaires de hooks qui s’exécutent lorsque l’événement satisfait au critère du groupe
{
  "description": "Optional lifecycle hooks for this workspace.",
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/session_start.py",
            "statusMessage": "Loading session notes",
            "additionalContextLimit": 5000
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/session_end.py",
            "timeout": 3
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"",
            "statusMessage": "Checking Bash command"
          }
        ]
      }
    ],
    "PermissionRequest": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/permission_request.py\"",
            "statusMessage": "Checking approval request"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py\"",
            "statusMessage": "Reviewing Bash output"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/user_prompt_submit_data_flywheel.py\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/stop_continue.py\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Précisions :

  • description est une métadonnée facultative à la racine du fichier hooks.json. Elle n’a aucun effet sur le choix des hooks à exécuter.
  • timeout s’exprime en secondes.
  • Si vous omettez timeout, Codex applique un délai de 600 secondes pour la plupart des hooks.
    • Pour SessionEnd et Interrupt, le délai par défaut est de 1 seconde et le maximum de 3 secondes.
  • statusMessage est facultatif.
  • additionalContextLimit limite la quantité de texte qu’un hook de commande peut transmettre au modèle dans additionalContext. Au-delà, Codex enregistre le texte intégral sur disque et transmet un aperçu plus court. Consultez la section Sorties volumineuses des hooks.
  • commandWindows permet de remplacer la commande uniquement sur Windows. Ce champ est facultatif. En TOML, utilisez command_windows ou commandWindows.
  • Définissez async sur true pour exécuter un hook de commande en arrière-plan.
  • Codex prend en charge les gestionnaires command et mcp_tool. Il analyse les gestionnaires prompt et agent, puis les ignore.
  • Les commandes utilisent le cwd de la session comme répertoire de travail.
  • Pour les hooks propres au dépôt, déterminez le chemin à partir de la racine Git plutôt que d’utiliser un chemin relatif comme .codex/hooks/.... Ainsi, Codex retrouve le hook même si vous le lancez depuis un sous-répertoire.

Configuration TOML équivalente directement dans config.toml :

[[hooks.SessionStart]]
matcher = "^compact$"

[[hooks.SessionStart.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/session_start.py"'
additionalContextLimit = 5000

[[hooks.PreToolUse]]
matcher = "^Bash$"

[[hooks.PreToolUse.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"'
timeout = 30
statusMessage = "Checking Bash command"

[[hooks.PostToolUse]]
matcher = "^Bash$"

[[hooks.PostToolUse.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py"'
timeout = 30
statusMessage = "Reviewing Bash output"

Hooks d’outils MCP

Un hook d’outil MCP permet d’appeler un outil sur un serveur MCP déjà connecté lorsqu’un événement du cycle de vie se produit. Il transmet des arguments structurés directement à l’outil. La procédure de vérification et le contrat de sortie sont les mêmes que pour un hook de commande.

Configurer un hook d’outil MCP

Ce hook demande au serveur MCP scanner d’analyser chaque correctif après la création ou la modification de fichiers par Codex :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "scanner",
            "tool": "scan_patch",
            "input": { "patch": "${tool_input.command}" },
            "timeout": 30,
            "statusMessage": "Scanning edited files"
          }
        ]
      }
    ]
  }
}
Champ Signification
type Doit valoir mcp_tool.
server Nom obligatoire d’un serveur MCP déjà connecté.
tool Nom obligatoire d’un outil exposé par ce serveur.
input Objet JSON facultatif contenant les modèles d’arguments. Valeur par défaut : {}.
timeout Durée maximale d’exécution active, en secondes. Facultative, avec une valeur par défaut de 600.
statusMessage Message facultatif affiché pendant l’exécution du hook.

Construire les arguments à partir de l’événement

Utilisez ${field.nested} pour lire un champ de l’événement en notation pointée. Si la variable occupe toute la valeur, celle-ci conserve son type JSON. Si la variable fait partie d’une chaîne plus longue, Codex la convertit en texte. Codex effectue ces substitutions récursivement dans les objets et les tableaux.

Pour un événement contenant {"tool_input":{"file_path":"src/main.rs","count":3}}, ce modèle d’arguments :

{
  "path": "${tool_input.file_path}",
  "count": "${tool_input.count}",
  "message": "Scanning ${tool_input.file_path}"
}

donne :

{
  "path": "src/main.rs",
  "count": 3,
  "message": "Scanning src/main.rs"
}

Exécution et cycle de vie

  • Les hooks utilisent une connexion MCP existante. Ils ne démarrent pas de serveur et ne rétablissent pas de connexion.
  • Un hook peut bloquer une opération si l’outil renvoie une décision de blocage. Une erreur, un serveur manquant ou un outil indisponible ne bloque pas l’opération.
  • Les hooks d’outils MCP s’exécutent de manière synchrone. Ils ne demandent pas d’approbation pour l’outil et ne déclenchent pas d’autres hooks.
  • Le délai le plus court entre celui du hook et celui du serveur s’applique. Le temps d’attente d’une réponse à une demande d’élicitation MCP n’est pas décompté.
  • Les hooks SessionStart peuvent s’exécuter avant qu’un serveur MCP soit prêt. Dans ce cas, ils ne bloquent pas la session.
  • SessionEnd ne prend pas en charge les hooks d’outils MCP.

Désactiver les hooks

Les hooks sont activés par défaut. Pour les désactiver dans config.toml, utilisez le réglage suivant :

[features]
hooks = false

Utilisez hooks comme clé de fonctionnalité. L’alias codex_hooks fonctionne encore, mais il est obsolète. Les administrateurs peuvent imposer la désactivation des hooks de la même façon dans requirements.toml avec [features].hooks = false.

Hooks gérés dans requirements.toml

Choisissez les hooks en fonction de l’environnement qui coordonne la tâche.

Hooks avec orchestration dans le cloud

Dans les espaces de travail où la politique gérée et les hooks distants sont activés, Work Cloud avec accès à l’ordinateur local et les dots peuvent appeler un service MCP lors des événements de tâche et d’outil pris en charge. Ces appels utilisent les gestionnaires mcp_tool que l’administrateur de votre espace de travail configure dans Agent Security. Work Cloud sans accès à l’ordinateur local et les comptes personnels n’utilisent pas ces hooks d’entreprise.

L’orchestration dans le cloud prend uniquement en charge les hooks MCP de portée globale, même lorsqu’un outil s’exécute sur un ordinateur connecté. Elle ne prend en charge ni les gestionnaires de commandes, de prompts ou d’agents, ni les hooks issus de la configuration locale, des plugins ou des répertoires locaux, ni les hooks propres à un environnement, ni les hooks MCP SessionEnd. Lorsque l’orchestration et l’exécution sont toutes deux locales, les hooks existants continuent de fonctionner dans les fils Work et Codex entièrement locaux.

Un hook pris en charge peut bloquer une action en renvoyant un refus explicite. Si un hook PreToolUse rencontre une erreur, dépasse le délai imparti ou renvoie une réponse mal formée, l’outil peut tout de même s’exécuter. Les hooks MCP ne fournissent qu’une partie de la piste d’audit. Pour connaître les vérifications à effectuer lors du déploiement et la portée de l’audit, consultez la section Vérifier les hooks et la compatibilité réseau.

Hooks gérés dans Codex en local

Définissez les hooks de Codex pour l’exécution locale dans la section [hooks] de requirements.toml. Pour imposer les hooks gérés même aux utilisateurs qui ont désactivé les hooks localement, fixez [features].hooks = true dans requirements.toml en plus de [hooks]. Pour ignorer les hooks de l’utilisateur, du projet, de la session et des plugins tout en autorisant ceux que gère l’administrateur, définissez allow_managed_hooks_only = true.

allow_managed_hooks_only = true

[features]
hooks = true

[hooks]
managed_dir = "/enterprise/hooks"
windows_managed_dir = 'C:\enterprise\hooks'

[[hooks.PreToolUse]]
matcher = "^Bash$"

[[hooks.PreToolUse.hooks]]
type = "command"
command = "python3 /enterprise/hooks/pre_tool_use_policy.py"
command_windows = 'py -3 C:\enterprise\hooks\pre_tool_use_policy.py'
timeout = 30
statusMessage = "Checking managed Bash command"

Précisions sur les hooks gérés :

  • managed_dir s’utilise sur macOS et Linux.
  • windows_managed_dir s’utilise sur Windows.
  • Codex ne distribue pas les scripts de managed_dir. Les outils de votre entreprise doivent les installer et les mettre à jour séparément.
  • Les commandes des hooks gérés doivent utiliser les chemins absolus des scripts dans le répertoire géré configuré.
  • Avec allow_managed_hooks_only = true, Codex ignore les hooks de l’utilisateur, du projet, de la session et des plugins. Il charge toujours les hooks gérés provenant de requirements.toml et des autres couches de configuration gérées.

Hooks fournis par les plugins

Lorsqu’un plugin est activé, Codex peut charger ses hooks de cycle de vie en plus des hooks de l’utilisateur, des hooks du projet et des hooks gérés.

Par défaut, Codex recherche hooks/hooks.json à la racine du plugin. Le manifeste du plugin peut remplacer cet emplacement par défaut avec une entrée hooks dans .codex-plugin/plugin.json. Cette entrée peut contenir un chemin commençant par ./, un tableau de chemins commençant par ./, un objet définissant directement des hooks ou un tableau d’objets de ce type.

{
  "name": "repo-policy",
  "hooks": "./hooks/hooks.json"
}

Les chemins des hooks indiqués dans le manifeste sont relatifs à la racine du plugin et doivent rester dans son arborescence. Si le manifeste définit hooks, Codex utilise ces entrées à la place du fichier par défaut hooks/hooks.json.

Les commandes des hooks de plugins reçoivent les variables d’environnement suivantes :

  • PLUGIN_ROOT est une extension propre à Codex qui pointe vers la racine du plugin installé.
  • PLUGIN_DATA est une extension propre à Codex qui pointe vers le répertoire de données du plugin accessible en écriture.
  • Codex définit également CLAUDE_PLUGIN_ROOT et CLAUDE_PLUGIN_DATA pour assurer la compatibilité avec les hooks de plugins existants.

Les hooks de plugins utilisent le même schéma d’événement que les autres hooks. Examinez-les et déclarez-les fiables avant leur exécution. Dans la CLI, ouvrez /hooks. Dans l’application de bureau, sélectionnez Examiner les hooks dans la zone de saisie ou ouvrez Paramètres > Hooks. Si une mise à jour du plugin modifie un script fourni, Codex exige un nouvel examen, même si la définition du hook reste identique.

Motifs de correspondance

Le champ matcher contient une expression régulière qui détermine quand déclencher les hooks. Utilisez "*" ou "", ou omettez complètement matcher, pour sélectionner toutes les occurrences d’un événement pris en charge.

Actuellement, seuls certains événements Codex tiennent compte de matcher :

Événement Filtre appliqué par matcher Précisions
PermissionRequest Nom de l’outil Accepte notamment Bash, apply_patch* et les noms d’outils MCP
PostToolUse Nom de l’outil Consultez la section Outils pris en charge
PostCompact Déclencheur du compactage Valeurs possibles : manual ou auto
PreModelRequest, PostModelRequest Filtrage non pris en charge Les deux événements se déclenchent à chaque tentative de requête au modèle
PreCompact Déclencheur du compactage Valeurs possibles : manual ou auto
PreToolUse Nom de l’outil Consultez la section Outils pris en charge
SessionEnd Motif de fin Seule valeur actuelle : other
SessionStart Origine du démarrage Valeurs possibles : startup, resume, clear, compact et fork
SubagentStart Type de sous-agent Les valeurs dépendent du sous-agent qui démarre
SubagentStop Type de sous-agent Les valeurs dépendent du sous-agent qui s’arrête
UserPromptSubmit Filtrage non pris en charge Codex ignore tout matcher configuré pour cet événement
Stop Filtrage non pris en charge Codex ignore tout matcher configuré pour cet événement
Interrupt Filtrage non pris en charge Codex ignore tout matcher configuré pour cet événement

*Pour apply_patch, matcher accepte aussi Edit ou Write.

Exemples :

  • Bash
  • ^apply_patch$
  • Edit|Write
  • mcp__filesystem__read_file
  • mcp__filesystem__.*
  • startup|resume|clear|compact|fork
  • manual|auto

Outils pris en charge

PreToolUse et PostToolUse peuvent observer d’autres appels que ceux du shell et des outils MCP. La plupart des outils de type fonction exécutés localement passent par le même mécanisme de hooks. Vous pouvez donc les sélectionner par leur nom, inspecter leurs arguments JSON et, avec PreToolUse, bloquer ou réécrire l’appel.

Mode d’appel de l’outil PreToolUse PostToolUse Précisions
Commandes shell Oui Oui Utilisez Bash comme motif.
Exécution unifiée (exec_command) Oui Oui Utilisez Bash comme motif. Une interrogation ultérieure avec write_stdin peut transmettre l’événement PostToolUse de la commande initiale une fois celle-ci terminée.
apply_patch Oui Oui Utilisez apply_patch, Edit ou Write comme motif.
Outils MCP Oui Oui Utilisez le nom de l’outil MCP, par exemple mcp__filesystem__read_file.
Autres outils de type fonction exécutés localement Oui Oui Utilisez le nom de la fonction, par exemple update_plan. spawn_agent correspond aussi à Agent.
Outils hébergés, comme WebSearch Non Non Ces outils ne passent pas par le mécanisme de hooks des fonctions locales.

write_stdin communique avec une session d’exécution unifiée existante. Il ne déclenche pas de nouveau PreToolUse lorsqu’il envoie des données ou interroge une commande déjà passée par PreToolUse.

Certains modes d’appel spécialisés peuvent contourner le mécanisme de hooks par défaut. Les hooks d’outils offrent une protection utile, mais ne suffisent pas à garantir l’application de toutes vos règles.

Champs d’entrée communs

Chaque hook de commande reçoit un objet JSON sur stdin.

Voici les champs communs que vous utiliserez le plus souvent :

Champ Type Signification
session_id string Identifiant de la session Codex en cours. Les hooks des sous-agents utilisent l’identifiant de la session parente.
transcript_path string | null Chemin du fichier de transcription de la session, s’il existe
cwd string Répertoire de travail de la session
hook_event_name string Nom de l’événement de hook en cours
model string Extension propre à Codex. Slug du modèle actif

Pour les hooks liés à un tour, les tableaux propres à chaque événement indiquent turn_id comme extension propre à Codex.

SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop, Stop et Interrupt incluent également permission_mode. Ce champ indique le mode d’autorisation actuel : default, acceptEdits, plan, dontAsk ou bypassPermissions.

transcript_path facilite l’accès à la transcription de la discussion. Son format ne constitue toutefois pas une interface stable pour les hooks et peut évoluer.

Pour connaître le format d’échange complet, consultez la section Schémas.

Champs de sortie communs

SessionStart, PreCompact, PostCompact, UserPromptSubmit, SubagentStop et Stop prennent en charge les champs JSON communs ci-dessous. SubagentStart accepte la même structure pour systemMessage et le contexte propre au hook, mais continue: false n’arrête pas le sous-agent :

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
Champ Effet
continue Si la valeur est false, Codex marque cette exécution du hook comme arrêtée
stopReason Codex enregistre cette valeur comme motif de l’arrêt
systemMessage Codex affiche le message sous forme d’avertissement dans l’interface ou le flux d’événements
suppressOutput Codex analyse ce champ, mais sa fonctionnalité n’est pas encore implémentée

Un code de sortie 0 sans sortie indique une réussite et Codex poursuit l’opération.

PreToolUse et PermissionRequest acceptent systemMessage, mais continue, stopReason et suppressOutput ne sont pas encore pris en charge pour ces événements. Si un hook PreToolUse renvoie l’un de ces champs non pris en charge, Codex marque l’exécution du hook comme ayant échoué, signale l’erreur et poursuit l’appel d’outil.

PostToolUse accepte systemMessage, continue: false et stopReason. Codex analyse suppressOutput, mais ne le prend pas encore en charge pour cet événement.

Sorties volumineuses des hooks

Par défaut, Codex limite à environ 2 500 tokens chaque message de sortie d’un hook transmis au modèle. Si un hook dépasse ce seuil, Codex enregistre le texte intégral dans <temp_dir>/hook_outputs/<session_id>/<uuid>.txt et transmet au modèle un aperçu du début et de la fin du texte ainsi que le chemin du fichier. Ce mécanisme de débordement sur disque permet à Codex de stocker la sortie volumineuse sur disque et de la remplacer, pour le modèle, par un aperçu plus court. Si l’écriture du fichier échoue, le modèle reçoit tout de même un aperçu tronqué.

Limitez la quantité de contexte fournie par les hooks et les plugins. Leurs contributions s’additionnent et peuvent dégrader les performances du modèle. Augmenter additionalContextLimit accroît ce risque. Évitez de fixer la limite à 0 sauf si le hook impose une limite de sortie stricte. Sinon, un seul hook peut occuper toute la fenêtre de contexte.

Pour tout hook de commande qui renvoie additionalContext, définissez additionalContextLimit dans le gestionnaire pour ajuster le seuil approximatif en tokens :

{
  "type": "command",
  "command": "python3 ~/.codex/hooks/session_start.py",
  "additionalContextLimit": 5000
}

Omettez additionalContextLimit pour utiliser le seuil par défaut de 2500 tokens. Utilisez un entier positif pour définir un autre seuil, ou 0 pour transmettre directement au modèle tout le contexte supplémentaire du gestionnaire. Codex évalue chaque gestionnaire correspondant séparément. Pour les événements qui ne peuvent pas produire de contexte supplémentaire, Codex ignore additionalContextLimit et signale un avertissement de configuration.

Ce réglage s’applique uniquement à additionalContext. Les retours sur les outils et les prompts de poursuite conservent la limite par défaut.

Comme les sorties volumineuses peuvent être enregistrées sur disque, évitez de renvoyer des secrets ou d’autres données sensibles dans la sortie des hooks.

Exécuter des hooks en arrière-plan

Par défaut, Codex attend la fin d’un hook de commande avant de poursuivre l’opération qui l’a déclenché. Définissez async sur true pour exécuter le hook de commande en arrière-plan pendant que Codex poursuit l’opération.

Configurer un hook en arrière-plan

Ajoutez "async": true à un gestionnaire de commande dans hooks.json :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/post_tool_use.py",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Pour un hook défini directement dans config.toml, indiquez async = true :

[[hooks.PostToolUse]]
matcher = "Bash"

[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120

Les hooks en arrière-plan utilisent les mêmes entrées, critères de correspondance, vérifications de confiance, délais et règles de traitement des sorties volumineuses que les hooks de commande synchrones. Comme pour les autres hooks de commande, timeout s’exprime en secondes et vaut 600 par défaut. Pour les hooks Interrupt, le délai par défaut est d’une seconde et le maximum de trois secondes, y compris en arrière-plan.

Fonctionnement des hooks en arrière-plan

Lorsqu’un hook en arrière-plan se termine, Codex transmet les informations de sortie prises en charge dès que la conversation le permet :

  • Si un tour est en cours, Codex attend la fin de la requête en cours au modèle et des appels d’outils, puis met la sortie à disposition de la prochaine requête au modèle dans ce tour.
  • Si aucun tour n’est en cours, Codex attend le prochain tour de l’utilisateur. La fin d’un hook en arrière-plan ne déclenche pas de nouveau tour.

Utilisez la même sortie JSON propre à l’événement que pour un hook synchrone. Codex ajoute additionalContext au contexte du modèle et affiche systemMessage sous forme d’avertissement.

Les hooks en arrière-plan ne peuvent ni bloquer, ni approuver, ni réécrire, ni contrôler d’une autre façon l’opération qui les a déclenchés. Utilisez des hooks synchrones pour appliquer des règles aux outils, prendre des décisions d’autorisation, rejeter des prompts ou prolonger un tour.

Limites

  • Codex exécute jusqu’à huit hooks en arrière-plan simultanément par session. Les hooks supplémentaires attendent qu’un hook en cours se termine.
  • Chaque invocation correspondant à un événement s’exécute indépendamment. Les hooks en arrière-plan peuvent se terminer dans un ordre différent de leur ordre de lancement.
  • À la fin de la session, Codex annule les hooks en arrière-plan encore en cours et abandonne les sorties qui n’ont pas encore été transmises.
  • Les hooks SessionEnd s’exécutent toujours de manière synchrone.

Hooks

SessionStart

Pour cet événement, matcher s’applique à source.

Champs en complément des champs d’entrée communs :

Champ Type Signification
source string Origine du démarrage de la session : startup, resume, clear, compact ou fork

Un fil forké utilise source: "fork". La reprise d’un fil avec un historique fourni utilise toujours source: "resume".

Codex ajoute le texte brut écrit sur stdout au contexte développeur.

La sortie JSON sur stdout accepte les champs de sortie communs et la structure suivante, propre à ce hook :

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Load the workspace conventions before editing."
  }
}

Codex ajoute le texte de additionalContext au contexte développeur.

Après le compactage d’une session racine, les hooks SessionStart correspondant à source: "compact" s’exécutent avant la prochaine requête au modèle. Cela vaut également pour un compactage automatique en cours de tour : Codex transmet le contexte supplémentaire du hook dès la reprise, sans attendre un prochain tour de l’utilisateur. Si le hook renvoie continue: false, Codex termine le tour sans envoyer de nouvelle requête au modèle.

SessionEnd

SessionEnd permet d’exécuter une commande à la fin d’une session, par exemple pour enregistrer des notes finales ou nettoyer des fichiers. Il s’exécute pour le fil principal lorsque vous archivez ou supprimez une conversation encore ouverte, lorsque Codex se ferme normalement, ou lorsqu’une conversation reste inactive et fermée dans tous les clients connectés pendant 30 minutes. Il ne s’exécute pas pour les sous-agents.

Passer à une autre conversation ou appeler thread/unsubscribe ne met pas immédiatement fin à la session et ne déclenche donc pas aussitôt SessionEnd. Votre hook peut encore lire la transcription de la session pendant son exécution.

Pour cet événement, matcher filtre reason. Pour le moment, reason vaut toujours other. Vous pouvez omettre matcher ou utiliser other pour que le hook s’exécute à chaque événement SessionEnd.

Champs en complément des champs d’entrée communs :

Champ Type Signification
reason string Motif de fin de session : other

Par exemple, une commande SessionEnd reçoit :

{
  "session_id": "thr_123",
  "transcript_path": "/workspace/.codex/rollout.jsonl",
  "cwd": "/workspace",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

Les hooks SessionEnd s’exécutent toujours de manière synchrone, même si async vaut true. Leur sortie est purement informative : elle n’oriente pas Codex et ne maintient pas le fil ouvert. Si une commande dépasse le délai imparti ou se termine en erreur, Codex signale un échec du hook.

SubagentStart

Pour cet événement, matcher s’applique à agent_type.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
agent_id string Identifiant du sous-agent
agent_type string Type ou profil du sous-agent
permission_mode string Mode d’autorisation actuel

Codex ajoute le texte brut écrit sur stdout au contexte développeur du sous-agent.

La sortie JSON sur stdout accepte systemMessage et la structure suivante, propre à ce hook :

{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Review the repository test conventions first."
  }
}

Codex ajoute le texte de additionalContext au contexte développeur du sous-agent. Codex analyse continue: false pour des raisons de compatibilité, mais cette valeur n’empêche pas le sous-agent de démarrer.

PreToolUse

PreToolUse peut intercepter les appels Bash, les modifications de fichiers effectuées avec apply_patch, les appels d’outils MCP et ceux d’autres outils de type fonction exécutés localement. Consultez la section Outils pris en charge pour connaître les modes d’appel concernés et les exceptions.

matcher s’applique à tool_name et à ses alias de correspondance. Pour les modifications de fichiers via apply_patch, matcher accepte apply_patch, Edit ou Write. L’entrée du hook indique toujours tool_name: "apply_patch".

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
tool_name string Nom canonique de l’outil pour le hook, par exemple Bash, apply_patch ou un nom MCP comme mcp__fs__read
tool_use_id string Identifiant de cet appel d’outil
tool_input JSON value Entrée propre à l’outil. Bash et apply_patch utilisent tool_input.command. Les outils MCP et les autres outils de type fonction exécutés localement transmettent leurs arguments.

Codex ignore le texte brut écrit sur stdout.

La sortie JSON sur stdout accepte systemMessage. Pour refuser un appel d’outil pris en charge, renvoyez la structure suivante, propre à ce hook :

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook."
  }
}

Codex accepte également l’ancienne structure de blocage :

{
  "decision": "block",
  "reason": "Destructive command blocked by hook."
}

Vous pouvez également utiliser le code de sortie 2 et écrire le motif du blocage sur stderr.

Pour ajouter du contexte visible par le modèle sans bloquer l’appel, renvoyez hookSpecificOutput.additionalContext :

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "additionalContext": "The pending command touches generated files."
  }
}

Pour réécrire un appel d’outil pris en charge sans le bloquer, renvoyez permissionDecision: "allow" avec updatedInput :

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "echo rewritten"
    }
  }
}

Pour les commandes Bash et apply_patch, updatedInput doit contenir un champ command de type chaîne. Pour les outils MCP et les autres outils de type fonction exécutés localement, updatedInput contient l’objet qui remplace les arguments. Renvoyez updatedInput uniquement avec permissionDecision: "allow". Codex signale toute autre forme de updatedInput comme une erreur.

Codex analyse permissionDecision: "ask", l’ancienne valeur decision: "approve", continue: false, stopReason et suppressOutput, mais ne les prend pas encore en charge. Il marque l’exécution du hook comme ayant échoué, signale l’erreur et poursuit l’appel d’outil.

PermissionRequest

PermissionRequest s’exécute lorsque Codex s’apprête à demander une approbation, par exemple pour une élévation de privilèges du shell ou un accès réseau géré. Le hook peut accepter la demande, la refuser ou laisser la procédure d’approbation habituelle suivre son cours sans prendre de décision. Il ne s’exécute pas pour les commandes qui ne nécessitent aucune approbation.

matcher s’applique à tool_name et à ses alias de correspondance. Les valeurs canoniques actuelles incluent Bash, apply_patch et les noms d’outils MCP comme mcp__server__tool. Pour apply_patch, les alias Edit et Write fonctionnent également.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
tool_name string Nom canonique de l’outil pour le hook, par exemple Bash, apply_patch ou un nom MCP comme mcp__fs__read
tool_input JSON value Entrée propre à l’outil. Bash et apply_patch utilisent tool_input.command, tandis que les outils MCP transmettent tous leurs arguments.
tool_input.description string | null Motif de la demande d’approbation, en clair, lorsque Codex en dispose

Codex ignore le texte brut écrit sur stdout.

Les entrées de certains outils peuvent contenir une description en clair, mais le champ tool_input.description n’est pas systématiquement présent.

Pour approuver la demande, renvoyez :

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow"
    }
  }
}

Pour refuser la demande, renvoyez :

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "deny",
      "message": "Blocked by repository policy."
    }
  }
}

Si plusieurs hooks correspondants renvoient une décision, tout deny l’emporte. En l’absence de refus, un allow autorise la demande sans afficher d’invite d’approbation. Si aucun hook correspondant ne se prononce, Codex suit la procédure d’approbation habituelle.

Ne renvoyez pas updatedInput, updatedPermissions ou interrupt pour PermissionRequest. Ces champs sont réservés à un usage futur. Pour le moment, leur présence entraîne le refus de la demande.

PostToolUse

PostToolUse s’exécute après la production d’une sortie par un outil pris en charge, notamment Bash, apply_patch, les outils MCP et les autres outils de type fonction exécutés localement. Pour Bash, il s’exécute aussi après les commandes dont le code de sortie est différent de zéro. Il ne peut pas annuler les effets d’un outil déjà exécuté. Consultez la section Outils pris en charge pour connaître les modes d’appel concernés et les exceptions.

matcher s’applique à tool_name et à ses alias de correspondance. Pour les modifications de fichiers via apply_patch, matcher accepte apply_patch, Edit ou Write. L’entrée du hook indique toujours tool_name: "apply_patch".

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
tool_name string Nom canonique de l’outil pour le hook, par exemple Bash, apply_patch ou un nom MCP comme mcp__fs__read
tool_use_id string Identifiant de cet appel d’outil
tool_input JSON value Entrée propre à l’outil. Bash et apply_patch utilisent tool_input.command. Les outils MCP et les autres outils de type fonction exécutés localement transmettent leurs arguments.
tool_response JSON value Sortie propre à l’outil. Les outils MCP transmettent le résultat de l’appel MCP. Les autres outils de type fonction exécutés localement transmettent généralement leur sortie destinée au modèle.

Codex ignore le texte brut écrit sur stdout.

La sortie JSON sur stdout accepte systemMessage et la structure suivante, propre à ce hook :

{
  "decision": "block",
  "reason": "The Bash output needs review before continuing.",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "The command updated generated files."
  }
}

Codex ajoute le texte de additionalContext au contexte développeur.

Pour cet événement, decision: "block" n’annule pas la commande Bash déjà exécutée. Codex enregistre le retour du hook, remplace le résultat de l’outil par ce retour, puis transmet le message du hook au modèle pour poursuivre le tour.

Vous pouvez également utiliser le code de sortie 2 et écrire le motif de ce retour sur stderr.

Pour interrompre le traitement normal du résultat initial de l’outil après l’exécution de la commande, renvoyez continue: false. Codex remplacera le résultat par votre retour ou votre message d’arrêt, puis poursuivra à partir de ce texte.

Codex analyse updatedMCPToolOutput et suppressOutput, mais ne les prend pas encore en charge. Il marque l’exécution du hook comme ayant échoué, signale l’erreur et poursuit le traitement normal du résultat de l’outil.

Appels d’outils en mode code

Lorsqu’un modèle utilise le mode code pour appeler un outil depuis JavaScript, les décisions des hooks s’appliquent à cet appel imbriqué. PreToolUse peut bloquer l’outil avant son exécution ou réécrire son entrée. Un hook PostToolUse bloquant ne peut pas annuler les effets de l’outil, mais peut empêcher le script en cours de recevoir le résultat initial.

Résultat du hook Effet en mode code
PreToolUse bloque l’appel La promesse de l’appel d’outil est rejetée avant l’exécution de l’outil.
PreToolUse renvoie updatedInput L’outil s’exécute avec l’entrée réécrite et la promesse est résolue avec le résultat obtenu.
PostToolUse renvoie decision: "block" ou se termine avec le code 2 L’outil s’exécute, puis la promesse est rejetée avec le motif fourni par le hook.
PostToolUse renvoie continue: false Codex utilise le retour du hook comme résultat visible par le modèle, sans rejeter la promesse de l’appel imbriqué.

PreModelRequest et PostModelRequest

Utilisez ces hooks pour journaliser les requêtes au modèle, y compris les nouvelles tentatives au cours d’un même tour. PreModelRequest s’exécute avant chaque tentative. PostModelRequest en indique le résultat. Les deux hooks reçoivent le modèle et turn_id, ainsi que agent_id et agent_type lorsque ces informations sont disponibles. Leur sortie sert uniquement au diagnostic. Elle ne peut ni modifier le contexte du modèle ni bloquer une requête.

PostModelRequest inclut trois champs supplémentaires :

Champ Signification
status completed, failed ou cancelled.
error Message d’erreur si disponible, sinon null.
token_usage Décompte des tokens pour cette tentative si disponible, sinon null.

PreCompact

PreCompact s’exécute avant que Codex ne compacte la discussion. matcher s’applique à trigger, qui peut valoir manual ou auto.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
trigger string Déclencheur du compactage : manual ou auto

Codex ignore le texte brut écrit sur stdout.

La sortie JSON sur stdout accepte les champs de sortie communs. Si un hook PreCompact correspondant renvoie continue: false, Codex s’arrête avant le compactage.

PostCompact

PostCompact s’exécute après le compactage de la discussion par Codex. matcher s’applique à trigger, qui peut valoir manual ou auto.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
trigger string Déclencheur du compactage : manual ou auto

Codex ignore le texte brut écrit sur stdout.

La sortie JSON sur stdout accepte les champs de sortie communs. Si un hook PostCompact correspondant renvoie continue: false, Codex s’arrête après le compactage.

UserPromptSubmit

Codex n’utilise pas matcher pour cet événement actuellement.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
prompt string Prompt de l’utilisateur sur le point d’être envoyé

Codex ajoute le texte brut écrit sur stdout au contexte développeur.

La sortie JSON sur stdout accepte les champs de sortie communs et la structure suivante, propre à ce hook :

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Ask for a clearer reproduction before editing files."
  }
}

Codex ajoute le texte de additionalContext au contexte développeur.

Pour bloquer le prompt, renvoyez :

{
  "decision": "block",
  "reason": "Ask for confirmation before doing that."
}

Vous pouvez également utiliser le code de sortie 2 et écrire le motif du blocage sur stderr.

SubagentStop

Pour cet événement, matcher s’applique à agent_type.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
agent_id string Identifiant du sous-agent
agent_type string Type ou profil du sous-agent
agent_transcript_path string | null Chemin du fichier de transcription du sous-agent, s’il existe
stop_hook_active boolean Indique si le sous-agent a déjà poursuivi son exécution après un arrêt
last_assistant_message string | null Dernier message du sous-agent en tant qu’assistant, si disponible

SubagentStop attend du JSON sur stdout lorsque le hook se termine avec le code 0. Une sortie en texte brut n’est pas valide pour cet événement.

La sortie JSON sur stdout accepte les champs de sortie communs. Pour demander à Codex de poursuivre l’exécution du sous-agent, renvoyez :

{
  "decision": "block",
  "reason": "Run one more focused pass inside the subagent."
}

Vous pouvez également utiliser le code de sortie 2 et écrire le motif de la poursuite sur stderr.

Si l’un des hooks SubagentStop correspondants renvoie continue: false, cette décision prime sur les décisions de poursuite des autres hooks SubagentStop correspondants.

Stop

Codex n’utilise pas matcher pour cet événement actuellement.

Champs en complément des champs d’entrée communs :

Champ Type Signification
turn_id string Extension propre à Codex. Identifiant du tour Codex en cours
stop_hook_active boolean Indique si Stop a déjà prolongé ce tour
last_assistant_message string | null Texte du dernier message de l’assistant, si disponible

Stop attend du JSON sur stdout lorsque le hook se termine avec le code 0. Une sortie en texte brut n’est pas valide pour cet événement.

La sortie JSON sur stdout accepte les champs de sortie communs. Pour que Codex poursuive son travail, renvoyez :

{
  "decision": "block",
  "reason": "Run one more pass over the failing tests."
}

Vous pouvez également utiliser le code de sortie 2 et écrire le motif de la poursuite sur stderr.

Pour cet événement, decision: "block" ne rejette pas le tour. Cette décision demande à Codex de poursuivre. Codex crée automatiquement un prompt de poursuite qui fait office de nouveau prompt utilisateur et reprend le texte de reason.

Si l’un des hooks Stop correspondants renvoie continue: false, cette décision prime sur les décisions de poursuite des autres hooks Stop correspondants.

Interrupt

Interrupt s’exécute lorsque vous interrompez un tour en cours dans le fil principal. Utilisez-le pour consigner l’interruption ou nettoyer les ressources liées à un travail lancé par un hook. Il ne s’exécute pas pour les fils inactifs ni pour les sous-agents, et Codex ignore tout matcher configuré.

En plus des champs d’entrée communs, l’événement inclut turn_id, l’identifiant du tour interrompu, et permission_mode.

Pour cet événement, le délai par défaut des hooks de commande est d’une seconde. Vous pouvez le régler entre une et trois secondes. La sortie du hook ne peut ni empêcher l’interruption ni relancer le tour. Terminez avec le code 0 sans produire de sortie, ou renvoyez du JSON avec un champ facultatif systemMessage pour afficher un avertissement. Une sortie en texte brut n’est pas valide pour cet événement.

{ "systemMessage": "Saved the interrupted turn to the local audit log." }

Schémas

Les schémas de la branche main accessibles ci-dessous peuvent inclure des champs de hooks absents de la version actuelle. Pour connaître le comportement de la version publiée, référez-vous à cette page.

Pour connaître le format d’échange exact actuellement utilisé, consultez les schémas générés dans le dépôt GitHub de Codex.