Français

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 dans config.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, SubagentStart ou Stop
  • 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 :

  • description est une métadonnée facultative de premier niveau pour un fichier hooks.json. Elle ne modifie pas les hooks exécutés.
  • timeout est exprimé en secondes.
  • Si timeout est omis, Codex utilise 600 secondes pour la plupart des hooks.
    • SessionEnd et Interrupt utilisent par défaut 1 seconde et acceptent jusqu’à 3 secondes.
  • statusMessage est facultatif.
  • additionalContextLimit définit la quantité de additionalContext qu’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.
  • commandWindows est un remplacement de commande facultatif réservé à Windows. Dans TOML, utilisez command_windows ou commandWindows.
  • Définissez async sur true pour exécuter un hook de commande en arrière-plan.
  • Les gestionnaires command et mcp_tool sont pris en charge. Les gestionnaires prompt et agent sont analysés, mais ignorés.
  • Les commandes sont exécutées avec le cwd de 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 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’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 = false

Utilisez 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_dir est utilisé sous macOS et Linux.
  • windows_managed_dir est 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 = true ignore les hooks provenant de sources utilisateur, projet, session et plugin, mais charge toujours les hooks gérés depuis requirements.toml et 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_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 accessible en écriture du plugin.
  • Codex définit également CLAUDE_PLUGIN_ROOT et CLAUDE_PLUGIN_DATA pour 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|Write
  • mcp__filesystem__read_file
  • mcp__filesystem__.*
  • startup|resume|clear|compact
  • manual|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 = 120

Les 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 SessionEnd s’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