Français

Hooks

Exécutez des scripts déterministes au cours du cycle de vie de Codex

Les hooks constituent un framework d’extensibilité pour Codex. Ils vous permettent d’injecter vos propres scripts dans la boucle agentique, afin d’activer des fonctionnalités telles que :

  • Envoyer la conversation à un moteur personnalisé de journalisation ou d’analyse
  • Analyser les prompts de votre équipe pour empêcher le collage accidentel de clés API
  • Résumer automatiquement les conversations afin de créer des mémoires persistantes
  • Exécuter un contrôle de validation personnalisé à l’arrêt d’un tour de conversation, afin de faire respecter les normes
  • Personnaliser les prompts lorsque vous vous trouvez dans un répertoire donné

Comportements d’exécution à garder à l’esprit :

  • Tous les hooks correspondants provenant de plusieurs fichiers sont exécutés.
  • 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 de commande 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
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 des formes suivantes :

  • hooks.json
  • des tables [hooks] intégrées dans config.toml

Les plugins installés peuvent également inclure une configuration de cycle de vie dans leur manifeste ou dans un fichier hooks/hooks.json par défaut. Consultez Créer des plugins pour connaître les règles d’empaquetage 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

S’il existe plusieurs sources de hooks, Codex charge tous les hooks correspondants. Les couches de configuration de priorité supérieure ne remplacent pas les hooks des couches 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 de commande non géré puisse s’exécuter, Codex vous demande d’examiner et d’approuver sa définition exacte. Codex associe l’approbation au hachage actuel du hook ; ainsi, les hooks nouveaux ou modifiés sont 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 les 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 marqués comme gérés, approuvés par la stratégie et ne peuvent pas être désactivés depuis le navigateur de hooks de l’utilisateur.

Pour une automatisation ponctuelle qui vérifie déjà les sources des hooks en dehors de Codex, transmettez --dangerously-bypass-hook-trust afin d’exécuter les hooks activés sans exiger une approbation persistante des hooks pour cette invocation.

Structure de la configuration

Les hooks sont organisés sur 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 qui s’exécutent 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 niveau supérieur 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 utilise 1 seconde par défaut et prend en charge 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 complet sur le disque et n’envoie à la place 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.
  • L’option async est analysée, mais les hooks de commande asynchrones ne sont pas encore pris en charge.
  • Seuls les gestionnaires type: "command" s’exécutent actuellement. Les gestionnaires prompt et agent sont analysés, mais ignorés.
  • Les commandes utilisent le cwd de la session comme répertoire de travail.
  • Pour les hooks locaux au dépôt, privilégiez une résolution à partir de la racine Git plutôt que l’utilisation d’un chemin relatif tel que .codex/hooks/.... Codex peut être lancé depuis un sous-répertoire ; un chemin fondé sur la racine Git garantit la stabilité de 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"

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 fonctionne encore comme alias obsolète. Les administrateurs peuvent forcer la désactivation des hooks de la même manière dans requirements.toml avec [features].hooks = false.

Hooks gérés provenant de requirements.toml

Les exigences gérées par l’entreprise peuvent également définir des hooks directement sous [hooks]. Cela s’avère utile lorsque les administrateurs souhaitent imposer la configuration des hooks tout en déployant les scripts eux-mêmes par l’intermédiaire de MDM ou d’un autre système de gestion des appareils. Pour imposer les hooks gérés même aux utilisateurs qui les ont désactivés localement, épinglez [features].hooks = true dans requirements.toml avec [hooks]. Pour ignorer les hooks utilisateur, de projet, de session et de plugin tout en autorisant les hooks 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 concernant 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 ; les outils de votre 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 dans le répertoire géré configuré.
  • allow_managed_hooks_only = true ignore les hooks provenant des sources utilisateur, de projet, de session et de plugin, mais charge toujours les hooks gérés provenant de 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, de projet et gérés.

Par défaut, Codex recherche hooks/hooks.json dans la racine du plugin. Le manifeste d’un plugin peut remplacer cette valeur par défaut à l’aide d’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 par rapport à 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 de la valeur hooks/hooks.json par défaut.

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

  • PLUGIN_ROOT est une extension propre à Codex qui pointe vers la racine du plugin installé.
  • PLUGIN_DATA est une extension propre à Codex qui pointe vers le répertoire de données accessible en écriture du plugin.
  • Codex définit également CLAUDE_PLUGIN_ROOT et CLAUDE_PLUGIN_DATA pour assurer la compatibilité avec les hooks de plugin 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 les déclenchements 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 prennent en compte matcher :

Événement Ce que filtre matcher Remarques
PermissionRequest nom de l’outil La prise en charge inclut 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 motif de 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

*Pour apply_patch, les valeurs de 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 hook ; vous pouvez donc faire correspondre leur nom, inspecter leurs arguments JSON et, pour PreToolUse, bloquer ou réécrire l’appel.

Chemin de l’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, tel que mcp__filesystem__read_file.
Autres outils de fonction locaux Oui Oui Correspond au nom de l’outil de fonction, tel que update_plan. spawn_agent correspond également à Agent.
Outils hébergés, tels que WebSearch Non Non Ils n’utilisent pas le chemin de hook 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 hook par défaut. Considérez les hooks d’outils comme un garde-fou utile, et non comme une frontière de contrôle absolue.

Champs d’entrée communs

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

Voici les champs partagés que vous utiliserez généralement :

Champ Type Signification
session_id string Identifiant de la session Codex actuelle. Les hooks de sous-agents 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 limités à un tour indiquent turn_id comme extension propre à Codex dans leurs tableaux spécifiques à l’événement.

SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop et Stop 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 à titre pratique, mais le format de transcription n’est pas une interface stable pour les hooks et peut évoluer.

Si vous avez besoin du format de transmission 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, indique que cette exécution du hook a été arrêtée
stopReason Enregistré comme motif de l’arrêt
systemMessage Affiché comme avertissement dans l’interface utilisateur ou le flux d’événements
suppressOutput Analysé actuellement, mais pas encore implémenté

Une sortie avec le code 0 sans contenu est considérée comme une réussite et Codex poursuit son exécution.

PreToolUse et PermissionRequest prennent en charge systemMessage, mais continue, stopReason et suppressOutput ne sont actuellement pas pris en charge pour ces événements. Si un hook PreToolUse renvoie l’un de ces champs non pris en charge, Codex marque cette 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 actuellement pas pris en charge pour cet événement.

Sortie volumineuse d’un hook

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 intégral sous <temp_dir>/hook_outputs/<session_id>/<uuid>.txt et fournit au modèle un aperçu du début et de la fin avec le 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 des outils et les invites de continuation conservent la limite par défaut.

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

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 que Codex a compacté une session racine, les hooks SessionStart correspondant à source: "compact" s’exécutent avant la requête suivante au modèle. Cela s’applique également lorsque le compactage 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 une autre requête au modèle.

SessionEnd

SessionEnd vous permet d’exécuter une commande lorsqu’une session se termine, par exemple pour enregistrer les 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 après qu’une conversation est restée inactive et n’est ouverte dans aucun client connecté pendant 30 minutes. 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 toujours 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 une exécution à chaque événement SessionEnd.

Champs qui s’ajoutent aux champs d’entrée communs :

Champ Type Signification
reason string Motif de 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 sont consultatifs. Leur sortie n’orientera pas Codex et ne maintiendra pas le fil ouvert. Si une commande expire ou se termine avec une erreur, Codex la 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 sous-agent de démarrer.

PreToolUse

PreToolUse peut intercepter Bash, les modifications de fichiers effectuées via apply_patch, les appels d’outils MCP et d’autres outils de fonction locaux. Consultez la couverture des outils pour connaître les chemins pris en charge et les exceptions.

matcher s’applique à tool_name et aux alias de correspondance. Pour les modifications de fichiers via 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. ID 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 ID 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. 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 contenant les arguments de remplacement. Renvoyez updatedInput uniquement avec permissionDecision: "allow" ; les autres structures updatedInput sont signalées comme des erreurs.

permissionDecision: "ask", l’ancien decision: "approve", continue: false, stopReason et suppressOutput sont analysés, mais ne sont pas encore pris en charge. Codex 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 est sur le point de demander une approbation, par exemple pour une élévation de privilèges du shell ou une approbation du réseau géré. Il peut autoriser la demande, la refuser ou ne pas prendre de décision et laisser l’invite d’approbation habituelle se poursuivre. Il ne s’exécute pas pour les commandes qui ne nécessitent pas d’approbation.

matcher s’applique à tool_name et aux alias de correspondance. Les valeurs canoniques actuelles incluent Bash, apply_patch et des 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. ID 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 dispose

Le texte brut sur stdout est ignoré.

Certaines entrées d’outil peuvent inclure une description lisible, mais ne comptez pas sur la présence d’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, toute valeur deny l’emporte. Sinon, une valeur allow permet à la demande de continuer sans afficher l’invite d’approbation. Si aucun hook correspondant ne prend de décision, Codex utilise le flux d’approbation habituel.

Ne renvoyez pas updatedInput, updatedPermissions ou interrupt pour PermissionRequest ; ces champs sont réservés à un comportement futur et provoquent 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 d’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 la couverture des outils pour connaître les chemins pris en charge et les exceptions.

matcher s’applique à tool_name et aux alias de correspondance. Pour les modifications de fichiers via 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. ID 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 ID 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. 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 ainsi que 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. À la place, Codex enregistre 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 d’outil d’origine après que la commande a déjà été exécutée, 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 ne sont 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 blocage par PostToolUse ne peut pas annuler les effets de bord de l’outil, mais il peut empêcher le résultat d’origine 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 discussion. matcher s’applique à 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. ID 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 discussion. matcher s’applique à 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. ID 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. ID du tour Codex actif
prompt string Invite utilisateur sur le point d’être envoyée

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 la structure propre à ce hook suivante :

{
  "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 également utiliser le code de sortie 2 et écrire la raison 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. ID 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 du 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 d’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 également utiliser le code de sortie 2 et écrire la raison de la poursuite dans stderr.

Si un hook SubagentStop correspondant renvoie continue: false, cela prévaut sur les décisions de poursuite 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. ID 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 de l’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 permettre à Codex de poursuivre, renvoyez :

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

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

Pour cet événement, decision: "block" ne rejette pas le tour. À la place, il demande à Codex de poursuivre et crée automatiquement un nouveau prompt de continuation qui agit comme un nouveau prompt utilisateur, en utilisant votre reason comme texte de ce prompt.

Si un hook Stop correspondant renvoie continue: false, cela prévaut sur les décisions de poursuite des autres hooks Stop correspondants.

Schémas

Si vous avez besoin du format d’échange actuel exact, consultez les schémas générés dans le dépôt GitHub de Codex.

Alias en texte brut

  • string | null