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 dansconfig.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,SubagentStartouStop - 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 :
descriptionest une métadonnée facultative à la racine du fichierhooks.json. Elle n’a aucun effet sur le choix des hooks à exécuter.timeouts’exprime en secondes.- Si vous omettez
timeout, Codex applique un délai de600secondes pour la plupart des hooks.- Pour
SessionEndetInterrupt, le délai par défaut est de1seconde et le maximum de3secondes.
- Pour
statusMessageest facultatif.additionalContextLimitlimite la quantité de texte qu’un hook de commande peut transmettre au modèle dansadditionalContext. Au-delà, Codex enregistre le texte intégral sur disque et transmet un aperçu plus court. Consultez la section Sorties volumineuses des hooks.commandWindowspermet de remplacer la commande uniquement sur Windows. Ce champ est facultatif. En TOML, utilisezcommand_windowsoucommandWindows.- Définissez
asyncsurtruepour exécuter un hook de commande en arrière-plan. - Codex prend en charge les gestionnaires
commandetmcp_tool. Il analyse les gestionnairespromptetagent, puis les ignore. - Les commandes utilisent le
cwdde 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
SessionStartpeuvent s’exécuter avant qu’un serveur MCP soit prêt. Dans ce cas, ils ne bloquent pas la session. SessionEndne 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_dirs’utilise sur macOS et Linux.windows_managed_dirs’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 derequirements.tomlet 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_ROOTest une extension propre à Codex qui pointe vers la racine du plugin installé.PLUGIN_DATAest une extension propre à Codex qui pointe vers le répertoire de données du plugin accessible en écriture.- Codex définit également
CLAUDE_PLUGIN_ROOTetCLAUDE_PLUGIN_DATApour 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|Writemcp__filesystem__read_filemcp__filesystem__.*startup|resume|clear|compact|forkmanual|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
SessionEnds’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.