Hooks
Hooks
Exécutez des scripts déterministes pendant le cycle de vie de Codex
Les hooks constituent un framework d’extensibilité pour Codex. Ils vous permettent d’exécuter des scripts ou des outils MCP pendant la boucle agentique, notamment pour :
- Envoyer la conversation à un moteur personnalisé de journalisation ou d’analyse
- Analyser les prompts de votre équipe afin d’empêcher le collage accidentel d’API keys
- Résumer automatiquement les conversations pour créer des mémoires persistantes
- Exécuter une vérification de validation personnalisée à la fin d’un tour de conversation afin de faire respecter des normes
- Personnaliser les prompts dans un répertoire donné
Comportements d’exécution à garder à l’esprit :
- Tous les hooks correspondants provenant de plusieurs fichiers s’exécutent.
- Plusieurs hooks de commande correspondant au même événement sont lancés simultanément ; un hook ne peut donc pas empêcher le démarrage d’un autre hook correspondant.
- Les hooks non gérés doivent être examinés et approuvés avant leur exécution.
Les hooks s’exécutent à différents moments d’une conversation :
| Moment | Hooks |
|---|---|
| Pendant un tour | PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop |
| Lorsque vous interrompez un tour actif | Interrupt (ne s’exécute pas pour les sous-agents) |
| Au démarrage d’une session ou d’un sous-agent | SessionStart, SubagentStart |
| À la fin du fil principal | SessionEnd (ne s’exécute pas pour les sous-agents) |
Où Codex recherche les hooks
Codex détecte les hooks à côté des couches de configuration actives, sous l’une de ces formes :
hooks.json- des tables
[hooks]intégrées dansconfig.toml
Les plugins installés peuvent également inclure une configuration du cycle de vie par l’intermédiaire de leur
manifeste ou d’un fichier hooks/hooks.json par défaut. Consultez Créer des
plugins pour connaître les règles de
conditionnement 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 de hooks existent, Codex charge tous les hooks correspondants.
Les couches de configuration de priorité supérieure ne remplacent pas les hooks de priorité inférieure.
Si une même couche contient à la fois hooks.json et des éléments [hooks] intégrés, Codex
les fusionne et affiche un avertissement au démarrage. Privilégiez une seule représentation par couche.
Codex peut également détecter les hooks inclus dans les plugins activés. Les hooks inclus dans les plugins sont chargés avec les autres sources de hooks et suivent le même processus d’examen et d’approbation que les autres hooks non gérés.
Les hooks locaux au projet ne sont chargés que lorsque la couche .codex/ du projet est approuvée. Dans
les projets non approuvés, Codex continue de charger les hooks utilisateur et système à partir de leurs propres
couches de configuration actives.
Examiner et approuver les hooks
Codex répertorie les hooks configurés avant de déterminer ceux qui peuvent s’exécuter. Avant qu’un hook non géré puisse s’exécuter, Codex exige que vous examiniez et approuviez sa définition exacte. Codex associe l’approbation au hash actuel du hook ; les hooks nouveaux ou modifiés sont donc signalés comme nécessitant un examen et ignorés jusqu’à leur approbation.
Utilisez /hooks dans la CLI pour inspecter les sources des hooks, examiner les hooks nouveaux ou modifiés,
les approuver ou désactiver individuellement des hooks non gérés. Si des hooks doivent être examinés au
démarrage, Codex affiche un avertissement vous invitant à ouvrir /hooks.
Les hooks gérés provenant de sources système, MDM, cloud ou requirements.toml sont signalés
comme gérés, approuvés par la stratégie et ne peuvent pas être désactivés dans le navigateur de hooks de l’utilisateur.
Pour une automatisation ponctuelle qui vérifie déjà les sources de hooks en dehors de Codex, transmettez
--dangerously-bypass-hook-trust afin d’exécuter les hooks activés sans exiger
une approbation persistante pour cette invocation.
Structure de la configuration
Les hooks sont organisés en trois niveaux :
- Un événement de hook tel que
PreToolUse,PostToolUse,PreCompact,SubagentStartouStop - Un groupe de correspondance qui détermine quand cet événement correspond
- Un ou plusieurs gestionnaires de hooks exécutés lorsque le groupe de correspondance correspond
{
"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
}
]
}
]
}
}Remarques :
descriptionest une métadonnée facultative de premier niveau pour un fichierhooks.json. Elle ne modifie pas les hooks exécutés.timeoutest exprimé en secondes.- Si
timeoutest omis, Codex utilise600secondes pour la plupart des hooks.SessionEndetInterruptutilisent par défaut1seconde et acceptent jusqu’à3secondes.
statusMessageest facultatif.additionalContextLimitdéfinit la quantité deadditionalContextqu’un hook de commande peut envoyer au modèle avant que Codex n’enregistre le texte intégral sur le disque et n’envoie à la place qu’un aperçu plus court. Consultez Sortie volumineuse des hooks.commandWindowsest un remplacement de commande facultatif réservé à Windows. Dans TOML, utilisezcommand_windowsoucommandWindows.- Définissez
asyncsurtruepour exécuter un hook de commande en arrière-plan. - Les gestionnaires
commandetmcp_toolsont pris en charge. Les gestionnairespromptetagentsont analysés, mais ignorés. - Les commandes sont exécutées avec le
cwdde la session comme répertoire de travail. - Pour les hooks locaux au dépôt, privilégiez une résolution depuis la racine git plutôt qu’un
chemin relatif tel que
.codex/hooks/.... Codex peut être démarré depuis un sous-répertoire ; un chemin basé sur la racine git maintient stable l’emplacement du hook.
Équivalent TOML intégré 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’outil MCP
Un hook d’outil MCP permet à un événement du cycle de vie d’appeler un outil sur un serveur MCP déjà connecté. Il envoie directement des arguments structurés à l’outil et utilise le même processus d’approbation et le même contrat de sortie qu’un hook de commande.
Configurer un hook d’outil MCP
Ce hook demande au serveur MCP scanner d’analyser chaque patch après que Codex a écrit ou
modifié des fichiers :
{
"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 être mcp_tool. |
server |
Nom requis d’un serveur MCP déjà connecté. |
tool |
Nom requis d’un outil exposé par ce serveur. |
input |
Objet JSON facultatif contenant des modèles d’arguments. Valeur par défaut : {}. |
timeout |
Délai d’exécution active facultatif, en secondes. Valeur par défaut : 600. |
statusMessage |
Message facultatif affiché pendant l’exécution du hook. |
Développer les arguments à partir de l’événement du hook
Utilisez ${field.nested} pour lire un champ avec notation par points dans l’événement du hook. Un placeholder
qui occupe toute une valeur conserve son type JSON. Un placeholder intégré dans une chaîne
plus longue est rendu sous forme de texte. Codex développe récursivement 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}"
}devient :
{
"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 ni ne reconnectent les serveurs.
- Un hook peut bloquer une opération lorsque l’outil renvoie une décision de blocage. Les erreurs, les serveurs manquants et les outils indisponibles ne bloquent pas l’opération.
- Les hooks d’outil 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 d’élicitation MCP n’est pas comptabilisé dans le délai.
- 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’outil MCP.
Désactiver les hooks
Les hooks sont activés par défaut. Pour les désactiver dans config.toml, définissez :
[features]
hooks = falseUtilisez hooks comme clé de fonctionnalité canonique. codex_hooks reste pris en charge comme
alias obsolète. Les administrateurs peuvent désactiver de force les hooks de la même manière dans
requirements.toml avec [features].hooks = false.
Hooks gérés depuis requirements.toml
Les exigences gérées par l’entreprise peuvent également définir des hooks directement sous [hooks].
Cette approche est utile lorsque les administrateurs veulent imposer la configuration des hooks tout en
déployant les scripts proprement dits par MDM ou un autre système de gestion des appareils.
Pour imposer les hooks gérés même aux utilisateurs qui ont désactivé les hooks localement, épinglez
[features].hooks = true dans requirements.toml avec [hooks]. Pour ignorer
les hooks utilisateur, projet, session et plugin tout en autorisant ceux gérés par les administrateurs,
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"Remarques sur les hooks gérés :
managed_direst utilisé sous macOS et Linux.windows_managed_direst utilisé sous Windows.- Codex ne distribue pas les scripts dans
managed_dir; vos outils d’entreprise doivent les installer et les mettre à jour séparément. - Les commandes des hooks gérés doivent utiliser des chemins de script absolus sous le répertoire géré configuré.
allow_managed_hooks_only = trueignore les hooks provenant de sources utilisateur, projet, session et plugin, mais charge toujours les hooks gérés depuisrequirements.tomlet d’autres couches de configuration gérées.
Hooks inclus dans les plugins
Lorsqu’un plugin est activé, Codex peut charger les hooks de cycle de vie de ce plugin avec les hooks utilisateur, projet et gérés.
Par défaut, Codex recherche hooks/hooks.json à la racine du plugin. Le manifeste d’un plugin
peut remplacer ce comportement par une entrée hooks dans
.codex-plugin/plugin.json. L’entrée du manifeste peut être un chemin préfixé par ./, un
tableau de chemins préfixés par ./, un objet de hooks intégré ou un tableau d’objets de
hooks intégrés.
{
"name": "repo-policy",
"hooks": "./hooks/hooks.json"
}Les chemins de hooks du manifeste sont résolus relativement à la racine du plugin et doivent rester
à l’intérieur de celle-ci. Si un manifeste définit hooks, Codex utilise ces entrées du manifeste
à la place du fichier hooks/hooks.json par défaut.
Les commandes des hooks de plugin reçoivent ces variables d’environnement :
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 accessible en écriture du plugin.- Codex définit également
CLAUDE_PLUGIN_ROOTetCLAUDE_PLUGIN_DATApour assurer la compatibilité avec les hooks de plugins existants.
Les hooks de plugin utilisent le même schéma d’événements que les autres hooks. L’installation ou l’activation d’un plugin n’approuve pas automatiquement ses hooks ; Codex ignore les hooks inclus dans le plugin jusqu’à ce que vous examiniez et approuviez leur définition actuelle.
Motifs de correspondance
Le champ matcher est une chaîne d’expression régulière qui filtre le déclenchement des hooks. Utilisez "*",
"" ou omettez entièrement matcher pour faire correspondre chaque occurrence d’un événement
pris en charge.
Seuls certains événements Codex actuels tiennent compte de matcher :
| Événement | Ce que filtre matcher |
Remarques |
|---|---|---|
PermissionRequest |
nom de l’outil | La prise en charge comprend Bash, apply_patch* et les noms d’outils MCP |
PostToolUse |
nom de l’outil | Consultez Couverture des outils |
PostCompact |
déclencheur de compactage | Les valeurs sont manual ou auto |
PreCompact |
déclencheur de compactage | Les valeurs sont manual ou auto |
PreToolUse |
nom de l’outil | Consultez Couverture des outils |
SessionEnd |
raison de la fin | Actuellement, uniquement other |
SessionStart |
source de démarrage | Les valeurs sont startup, resume, clear et compact |
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 |
non pris en charge | Tout matcher configuré est ignoré pour cet événement |
Stop |
non pris en charge | Tout matcher configuré est ignoré pour cet événement |
Interrupt |
non pris en charge | Tout matcher configuré est ignoré pour cet événement |
*Pour apply_patch, les valeurs matcher peuvent également utiliser Edit ou Write.
Exemples :
Bash^apply_patch$Edit|Writemcp__filesystem__read_filemcp__filesystem__.*startup|resume|clear|compactmanual|auto
Couverture des outils
PreToolUse et PostToolUse peuvent observer davantage que les appels shell et MCP. La plupart des
outils de fonction locaux utilisent le même chemin de hooks ; vous pouvez donc faire correspondre le nom de leur outil,
inspecter leurs arguments JSON et, pour PreToolUse, bloquer ou réécrire l’appel.
| Chemin d’outil | PreToolUse |
PostToolUse |
Remarques |
|---|---|---|---|
| Commandes shell | Oui | Oui | Correspond à Bash. |
Exécution unifiée (exec_command) |
Oui | Oui | Correspond à Bash. Une interrogation write_stdin ultérieure peut fournir le PostToolUse de la commande d’origine lorsque celle-ci se termine. |
apply_patch |
Oui | Oui | Correspond à apply_patch, Edit ou Write. |
| Outils MCP | Oui | Oui | Correspond au nom de l’outil MCP, par exemple mcp__filesystem__read_file. |
| Autres outils de fonction locaux | Oui | Oui | Correspond au nom de l’outil de fonction, par exemple update_plan. spawn_agent correspond également à Agent. |
Outils hébergés, tels que WebSearch |
Non | Non | Ceux-ci n’utilisent pas le chemin de hooks des outils de fonction locaux. |
write_stdin sert de transport pour une session d’exécution unifiée existante. Il ne réexécute pas
PreToolUse lorsqu’il envoie une entrée ou interroge une commande qui a déjà passé
PreToolUse.
Certains chemins d’outils spécialisés peuvent ne pas utiliser le chemin de hooks par défaut. Considérez les hooks d’outils comme un garde-fou utile, et non comme une frontière de contrôle exhaustive.
Champs d’entrée communs
Chaque hook de commande reçoit un objet JSON sur stdin.
Voici les champs partagés que vous utiliserez le plus souvent :
| Champ | Type | Signification |
|---|---|---|
session_id |
string |
Identifiant de la session Codex actuelle. Les hooks de sous-agent utilisent l’identifiant de la session parente. |
transcript_path |
string | null |
Chemin vers le fichier de transcription de la session, le cas échéant |
cwd |
string |
Répertoire de travail de la session |
hook_event_name |
string |
Nom de l’événement de hook actuel |
model |
string |
Extension propre à Codex. Slug du modèle actif |
Les hooks associés à un tour indiquent turn_id comme extension propre à Codex dans leurs
tables spécifiques à l’événement.
SessionStart, PreToolUse, PermissionRequest, PostToolUse,
UserPromptSubmit, SubagentStart, SubagentStop, Stop et Interrupt incluent également
permission_mode, qui décrit le mode d’autorisation actuel comme default,
acceptEdits, plan, dontAsk ou bypassPermissions.
transcript_path pointe vers une transcription de conversation par commodité, mais le
format de transcription n’est pas une interface stable pour les hooks et peut évoluer.
Si vous avez besoin du format filaire complet, consultez Schémas.
Champs de sortie communs
SessionStart, PreCompact, PostCompact, UserPromptSubmit,
SubagentStop et Stop prennent en charge ces champs JSON partagés. 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 false, marque cette exécution du hook comme arrêtée |
stopReason |
Enregistré comme motif de l’arrêt |
systemMessage |
Affiché comme avertissement dans l’UI ou le flux d’événements |
suppressOutput |
Analysé actuellement, mais pas encore implémenté |
Le code de sortie 0 sans aucune sortie est considéré comme une réussite et Codex continue.
PreToolUse et PermissionRequest prennent en charge systemMessage, mais continue,
stopReason et suppressOutput ne sont pas actuellement 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 de l’outil.
PostToolUse prend en charge systemMessage, continue: false et stopReason.
suppressOutput est analysé, mais n’est pas actuellement pris en charge pour cet événement.
Sortie de hook volumineuse
Par défaut, Codex limite chaque message de sortie de hook visible par le modèle à environ
2 500 tokens. Si un hook en renvoie davantage, Codex enregistre le texte complet sous
<temp_dir>/hook_outputs/<session_id>/<uuid>.txt et fournit au modèle un
aperçu du début et de la fin, accompagné du chemin du fichier enregistré. Ce comportement est appelé
déversement : Codex stocke la sortie surdimensionnée sur le disque et la remplace par un
aperçu plus court visible par le modèle. Si le fichier ne peut pas être écrit, le modèle reçoit tout de même
un aperçu tronqué.
Pour tout hook de commande qui renvoie additionalContext, définissez
additionalContextLimit sur le gestionnaire afin de personnaliser le seuil approximatif de
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 choisir un autre seuil, ou 0 pour transmettre directement au modèle
l’intégralité du contexte supplémentaire du gestionnaire. Codex évalue chaque
gestionnaire correspondant indépendamment. Pour les événements qui ne peuvent pas produire de contexte
supplémentaire, Codex ignore additionalContextLimit et signale un avertissement de
configuration.
Ce paramètre s’applique uniquement à additionalContext. Les retours d’outils et les prompts de continuation
conservent la limite par défaut.
Une sortie surdimensionnée pouvant être écrite sur le 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 un hook de commande en
arrière-plan pendant que Codex continue.
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 intégré dans config.toml, définissez async = true :
[[hooks.PostToolUse]]
matcher = "Bash"
[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120Les hooks en arrière-plan utilisent la même entrée, le même filtre, le même examen de confiance, le même délai d’expiration et la même
gestion des sorties volumineuses que les hooks de commande synchrones. Comme
pour les autres hooks de commande, timeout est mesuré en secondes et sa valeur par défaut est
600. Les hooks Interrupt utilisent par défaut une seconde, avec un maximum de trois secondes,
y compris lorsqu’ils s’exécutent en arrière-plan.
Fonctionnement des hooks en arrière-plan
Lorsqu’un hook en arrière-plan se termine, Codex transmet les sorties informatives prises en charge au prochain point sûr de la conversation :
- Si un tour est actif, Codex attend la fin de la requête actuelle au modèle et des appels d’outils, puis rend la sortie disponible pour la prochaine requête au modèle de ce tour.
- Si aucun tour n’est actif, Codex attend le prochain tour utilisateur. La fin d’un hook en arrière-plan ne démarre pas un 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 comme
avertissement.
Limitations
- Codex exécute jusqu’à huit hooks en arrière-plan simultanément par session. Les hooks supplémentaires attendent la fin d’un hook en cours.
- Chaque invocation correspondante s’exécute indépendamment, et les hooks en arrière-plan peuvent se terminer dans un ordre différent de celui de leur démarrage.
- À la fin de la session, Codex annule les hooks en arrière-plan inachevés et supprime les sorties qui n’ont pas été transmises.
- Les hooks
SessionEnds’exécutent toujours de manière synchrone.
Hooks
SessionStart
matcher est appliqué à source pour cet événement.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
source |
string |
Mode de démarrage de la session : startup, resume, clear ou compact |
Le texte brut sur stdout est ajouté comme contexte développeur supplémentaire.
Le JSON sur stdout prend en charge les champs de sortie communs et cette
structure propre au hook :
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Load the workspace conventions before editing."
}
}Ce texte additionalContext est ajouté comme contexte développeur supplémentaire.
Après la compaction d’une session racine par Codex, les hooks SessionStart correspondant à
source: "compact" s’exécutent avant la prochaine requête au modèle. Cela s’applique également lorsqu’une
compaction automatique se produit au milieu d’un tour : Codex transmet le contexte
supplémentaire du hook à la continuation immédiate au lieu d’attendre un
tour utilisateur ultérieur. Si le hook renvoie continue: false, Codex met fin au tour
sans envoyer d’autre requête au modèle.
SessionEnd
SessionEnd vous permet d’exécuter une commande à la fin d’une session, par exemple pour enregistrer les dernières
notes ou nettoyer des fichiers. Il s’exécute pour le thread principal lorsque vous archivez ou
supprimez une conversation encore ouverte, lorsque Codex se ferme normalement ou lorsqu’une
conversation est inactive depuis 30 minutes et n’est ouverte dans aucun client
connecté. Il ne s’exécute pas pour les sous-agents.
Quitter une conversation ou appeler thread/unsubscribe ne met pas fin
immédiatement à la session ; SessionEnd ne s’exécute donc pas tout de suite. Votre hook peut
encore lire la transcription de la session pendant son exécution.
matcher filtre reason pour cet événement. Pour l’instant, reason vaut toujours other.
Vous pouvez omettre matcher ou utiliser other pour chaque événement SessionEnd.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
reason |
string |
Motif de la fin de la 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 lorsque async vaut true. Ils
sont consultatifs : leur sortie n’oriente donc pas Codex et ne maintient pas le thread ouvert. Si une
commande dépasse le délai ou se termine avec une erreur, Codex le signale comme un échec du hook.
SubagentStart
matcher est appliqué à agent_type pour cet événement.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
agent_id |
string |
Identifiant du sous-agent |
agent_type |
string |
Type ou profil du sous-agent |
permission_mode |
string |
Mode d’autorisation actuel |
Le texte brut sur stdout est ajouté comme contexte développeur supplémentaire pour le sous-agent.
Le JSON sur stdout prend en charge systemMessage et cette structure propre au hook :
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Review the repository test conventions first."
}
}Ce texte additionalContext est ajouté comme contexte développeur supplémentaire pour le
sous-agent. continue: false est analysé à des fins de compatibilité, mais n’empêche pas le
démarrage du sous-agent.
PreToolUse
PreToolUse peut intercepter Bash, les modifications de fichiers effectuées au moyen de apply_patch,
les appels d’outils MCP et d’autres outils de fonction locaux. Consultez Couverture des
outils pour connaître les chemins pris en charge et les exceptions.
matcher est appliqué à tool_name et aux alias de correspondance. Pour les modifications de fichiers effectuées avec
apply_patch, les valeurs matcher peuvent utiliser apply_patch, Edit ou Write ; l’entrée du hook
indique toujours tool_name: "apply_patch".
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
tool_name |
string |
Nom canonique de l’outil du hook, tel que Bash, apply_patch ou un nom MCP comme mcp__fs__read |
tool_use_id |
string |
Identifiant de l’appel d’outil pour cette invocation |
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 fonction locaux envoient leurs arguments. |
Le texte brut sur stdout est ignoré.
Le JSON sur stdout peut utiliser systemMessage. Pour refuser un appel d’outil pris en charge, renvoyez
cette structure propre au hook :
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook."
}
}Codex accepte également cette ancienne structure de bloc :
{
"decision": "block",
"reason": "Destructive command blocked by hook."
}Vous pouvez aussi utiliser le code de sortie 2 et écrire le motif du blocage dans stderr.
Pour ajouter du contexte visible par le modèle sans bloquer, 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 inclure un champ de chaîne
command. Pour MCP et les autres outils de fonction locaux, updatedInput est l’objet
d’arguments de remplacement. Renvoyez updatedInput uniquement avec
permissionDecision: "allow" ; les autres structures updatedInput sont signalées comme
erreurs.
permissionDecision: "ask", l’ancien decision: "approve", continue: false,
stopReason et suppressOutput sont analysés, mais pas encore pris en charge. Codex marque
l’exécution du hook comme ayant échoué, signale l’erreur et poursuit l’appel de l’outil.
PermissionRequest
PermissionRequest s’exécute lorsque Codex est sur le point de demander une approbation, par exemple pour une
élévation shell ou l’approbation d’un réseau géré. Il peut autoriser la demande, la refuser
ou ne pas se prononcer et laisser le prompt d’approbation normal se poursuivre.
Il ne s’exécute pas pour les commandes qui ne nécessitent pas d’approbation.
matcher est appliqué à tool_name et aux alias de correspondance. Les valeurs canoniques actuelles
comprennent Bash, apply_patch et les noms d’outils MCP tels que
mcp__server__tool ; apply_patch correspond également à Edit et Write.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
tool_name |
string |
Nom canonique de l’outil du hook, tel que 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 envoient tous les arguments. |
tool_input.description |
string | null |
Motif d’approbation lisible, lorsque Codex en possède un |
Le texte brut sur stdout est ignoré.
Certaines entrées d’outils peuvent contenir une description lisible, mais ne comptez pas sur un
champ tool_input.description pour chaque outil.
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 des décisions, tout deny l’emporte. Sinon, un
allow autorise la demande sans afficher le prompt d’approbation. Si aucun
hook correspondant ne prend de décision, Codex utilise le processus d’approbation normal.
Ne renvoyez pas updatedInput, updatedPermissions ou interrupt pour
PermissionRequest ; ces champs sont réservés à un comportement futur et entraînent actuellement un refus
par défaut.
PostToolUse
PostToolUse s’exécute après que les outils pris en charge ont produit une sortie, notamment Bash,
apply_patch, les appels d’outils MCP et les autres outils de fonction locaux. Pour Bash, il
s’exécute également après les commandes qui se terminent avec un statut différent de zéro. Il ne peut pas annuler les effets
de bord d’un outil déjà exécuté. Consultez Couverture des outils pour
connaître les chemins pris en charge et les exceptions.
matcher est appliqué à tool_name et aux alias de correspondance. Pour les modifications de fichiers effectuées avec
apply_patch, les valeurs matcher peuvent utiliser apply_patch, Edit ou Write ; l’entrée du hook
indique toujours tool_name: "apply_patch".
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
tool_name |
string |
Nom canonique de l’outil du hook, tel que Bash, apply_patch ou un nom MCP comme mcp__fs__read |
tool_use_id |
string |
Identifiant de l’appel d’outil pour cette invocation |
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 fonction locaux envoient leurs arguments. |
tool_response |
JSON value |
Sortie propre à l’outil. Les outils MCP envoient le résultat de l’appel MCP. Les autres outils de fonction locaux envoient normalement leur sortie destinée au modèle. |
Le texte brut sur stdout est ignoré.
Le JSON sur stdout peut utiliser systemMessage et cette structure propre au hook :
{
"decision": "block",
"reason": "The Bash output needs review before continuing.",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "The command updated generated files."
}
}Ce texte additionalContext est ajouté comme contexte développeur supplémentaire.
Pour cet événement, decision: "block" n’annule pas la commande Bash terminée.
Codex enregistre plutôt le retour, remplace le résultat de l’outil par ce
retour et poursuit le modèle à partir du message fourni par le hook.
Vous pouvez aussi utiliser le code de sortie 2 et écrire le motif du retour dans stderr.
Pour interrompre le traitement normal du résultat original de l’outil après l’exécution de la commande,
renvoyez continue: false. Codex remplacera le résultat de l’outil par
votre retour ou votre texte d’arrêt, puis poursuivra à partir de là.
updatedMCPToolOutput et suppressOutput sont analysés, mais pas encore pris en charge.
Codex 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 depuis le 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 arrêter l’outil avant son exécution ou réécrire
son entrée. Un PostToolUse bloquant ne peut pas annuler les effets de bord de l’outil, mais il
peut empêcher le résultat original d’atteindre le script en cours d’exécution.
| Résultat du hook | Ce que voit le mode code |
|---|---|
PreToolUse bloque |
La promesse de l’outil est rejetée avant son exécution. |
PreToolUse renvoie updatedInput |
L’outil s’exécute avec l’entrée réécrite et la promesse est résolue avec ce résultat. |
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 du hook. |
PostToolUse renvoie continue: false |
Codex utilise le retour du hook comme résultat visible par le modèle, mais ne rejette pas la promesse de l’outil imbriqué. |
PreCompact
PreCompact s’exécute avant que Codex compacte la conversation. matcher est appliqué
à trigger, dont les valeurs sont manual et auto.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
trigger |
string |
Élément ayant déclenché la compaction : manual ou auto |
Le texte brut sur stdout est ignoré.
Le JSON sur stdout prend en charge les champs de sortie communs. Si un
hook PreCompact correspondant renvoie continue: false, Codex s’arrête avant
la compaction.
PostCompact
PostCompact s’exécute après que Codex a compacté la conversation. matcher est appliqué
à trigger, dont les valeurs sont manual et auto.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
trigger |
string |
Élément ayant déclenché la compaction : manual ou auto |
Le texte brut sur stdout est ignoré.
Le JSON sur stdout prend en charge les champs de sortie communs. Si un
hook PostCompact correspondant renvoie continue: false, Codex s’arrête après
la compaction.
UserPromptSubmit
matcher n’est actuellement pas utilisé pour cet événement.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
prompt |
string |
Prompt utilisateur sur le point d’être envoyé |
Le texte brut sur stdout est ajouté comme contexte développeur supplémentaire.
Le JSON sur stdout prend en charge les champs de sortie communs et
cette structure propre au hook :
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Ask for a clearer reproduction before editing files."
}
}Ce texte additionalContext est ajouté comme contexte développeur supplémentaire.
Pour bloquer le prompt, renvoyez :
{
"decision": "block",
"reason": "Ask for confirmation before doing that."
}Vous pouvez aussi utiliser le code de sortie 2 et écrire le motif du blocage dans stderr.
SubagentStop
matcher est appliqué à agent_type pour cet événement.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
agent_id |
string |
Identifiant du sous-agent |
agent_type |
string |
Type ou profil du sous-agent |
agent_transcript_path |
string | null |
Chemin vers le fichier de transcription du sous-agent, le cas échéant |
stop_hook_active |
boolean |
Indique si ce sous-agent a déjà été poursuivi |
last_assistant_message |
string | null |
Dernier message assistant du sous-agent, s’il est disponible |
SubagentStop attend du JSON sur stdout lorsqu’il se termine avec 0. Une sortie en texte brut est
invalide pour cet événement.
Le JSON sur stdout prend en charge les champs de sortie communs. Pour demander
à Codex de poursuivre le flux du sous-agent, renvoyez :
{
"decision": "block",
"reason": "Run one more focused pass inside the subagent."
}Vous pouvez aussi utiliser le code de sortie 2 et écrire le motif de la continuation dans stderr.
Si un hook SubagentStop correspondant renvoie continue: false, cela prévaut
sur les décisions de continuation des autres hooks SubagentStop
correspondants.
Stop
matcher n’est actuellement pas utilisé pour cet événement.
Champs qui s’ajoutent aux champs d’entrée communs :
| Champ | Type | Signification |
|---|---|---|
turn_id |
string |
Extension propre à Codex. Identifiant du tour Codex actif |
stop_hook_active |
boolean |
Indique si ce tour a déjà été poursuivi par Stop |
last_assistant_message |
string | null |
Texte du dernier message assistant, s’il est disponible |
Stop attend du JSON sur stdout lorsqu’il se termine avec 0. Une sortie en texte brut est invalide
pour cet événement.
Le JSON sur stdout prend en charge les champs de sortie communs. Pour que
Codex continue, renvoyez :
{
"decision": "block",
"reason": "Run one more pass over the failing tests."
}Vous pouvez aussi utiliser le code de sortie 2 et écrire le motif de la continuation dans stderr.
Pour cet événement, decision: "block" ne rejette pas le tour. Il indique plutôt à
Codex de continuer et crée automatiquement un nouveau prompt de continuation qui agit
comme un nouveau prompt utilisateur, en utilisant votre reason comme texte du prompt.
Si un hook Stop correspondant renvoie continue: false, cela prévaut
sur les décisions de continuation des autres hooks Stop correspondants.
Interruption
Interrupt s’exécute lorsque vous interrompez un tour actif dans le fil principal. Utilisez-le
pour consigner l’interruption ou nettoyer le travail lancé par un hook. Il ne s’exécute pas
pour les fils inactifs ni pour les sous-agents, et tout matcher configuré est ignoré.
Outre les champs d’entrée communs, l’événement inclut
turn_id, l’identifiant du tour interrompu, ainsi que permission_mode.
Le délai d’expiration par défaut des hooks de commande est d’une seconde. Les délais configurés sont
limités à une durée comprise entre une et trois secondes. La sortie du hook ne peut ni empêcher
l’interruption ni redémarrer le tour. Quittez avec 0 sans produire de sortie, ou renvoyez du JSON avec
un champ systemMessage facultatif 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
Si vous avez besoin du format filaire actuel exact, consultez les schémas générés dans le dépôt GitHub de Codex.
Alias en texte brut
- string | null