Hooks
Hooks
Ejecuta scripts deterministas durante el ciclo de vida de Codex
Los hooks son un marco de extensibilidad para Codex. Permiten ejecutar scripts o herramientas MCP durante el bucle del agente, lo que habilita funciones como:
- Enviar el chat a un motor personalizado de registro o análisis
- Analizar los prompts de tu equipo para impedir que se peguen accidentalmente API keys
- Resumir chats para crear automáticamente memorias persistentes
- Ejecutar una comprobación de validación personalizada cuando se detiene un turno del chat para aplicar estándares
- Personalizar los prompts cuando se trabaja en un directorio determinado
Comportamiento del entorno de ejecución que debes tener en cuenta:
- Se ejecutan todos los hooks coincidentes de varios archivos.
- Los distintos hooks de comandos que coinciden con el mismo evento se inician simultáneamente, por lo que un hook no puede impedir que se inicie otro hook coincidente.
- Los hooks no administrados deben revisarse y considerarse de confianza antes de ejecutarse.
Los hooks se ejecutan en distintos momentos de una conversación:
| Cuándo | Hooks |
|---|---|
| Durante un turno | PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop |
| Al interrumpir un turno activo | Interrupt (no se ejecuta para subagentes) |
| Al iniciar una sesión o subagente | SessionStart, SubagentStart |
| Cuando termina el hilo principal | SessionEnd (no se ejecuta para subagentes) |
Dónde busca Codex los hooks
Codex detecta hooks junto a las capas de configuración activas en cualquiera de estas formas:
hooks.json- tablas
[hooks]insertadas dentro deconfig.toml
Los plugins instalados también pueden incluir una configuración del ciclo de vida mediante su
manifiesto o un archivo hooks/hooks.json predeterminado. Consulta Crear
plugins para conocer las
reglas de empaquetado de plugins.
En la práctica, las cuatro ubicaciones más útiles son:
~/.codex/hooks.json~/.codex/config.toml<repo>/.codex/hooks.json<repo>/.codex/config.toml
Si existe más de un origen de hooks, Codex carga todos los hooks coincidentes.
Las capas de configuración con mayor precedencia no sustituyen los hooks de las capas con menor precedencia.
Si una misma capa contiene tanto hooks.json como [hooks] insertados, Codex
los combina y muestra una advertencia al iniciarse. Usa preferentemente una sola representación por capa.
Codex también puede detectar hooks incluidos en los plugins habilitados. Los hooks incluidos en plugins se cargan junto con otros orígenes de hooks y siguen el mismo flujo de revisión de confianza que los demás hooks no administrados.
Los hooks locales del proyecto solo se cargan cuando la capa .codex/ del proyecto es de confianza. En
proyectos que no son de confianza, Codex sigue cargando los hooks del usuario y del sistema desde sus propias
capas de configuración activas.
Revisar los hooks y confiar en ellos
Codex enumera los hooks configurados antes de decidir cuáles pueden ejecutarse. Antes de que pueda ejecutarse un hook no administrado, Codex exige que revises y marques como confiable su definición exacta. Codex registra la confianza con respecto al hash actual del hook, por lo que los hooks nuevos o modificados se marcan para revisión y se omiten hasta que se confíe en ellos.
Usa /hooks en la CLI para inspeccionar los orígenes de los hooks, revisar hooks nuevos o modificados,
marcarlos como confiables o deshabilitar hooks no administrados concretos. Si hay hooks pendientes de revisión al
iniciarse, Codex muestra una advertencia que te indica que abras /hooks.
Los hooks administrados procedentes del sistema, MDM, la nube o fuentes requirements.toml se marcan
como administrados, son de confianza por directiva y no pueden deshabilitarse desde el explorador de hooks del usuario.
Para automatizaciones puntuales que ya verifican los orígenes de los hooks fuera de Codex, proporciona
--dangerously-bypass-hook-trust para ejecutar los hooks habilitados sin exigir
que se conserve la confianza en ellos durante esa invocación.
Estructura de configuración
Los hooks se organizan en tres niveles:
- Un evento de hook, como
PreToolUse,PostToolUse,PreCompact,SubagentStartoStop - Un grupo de coincidencia que decide cuándo coincide ese evento
- Uno o varios controladores de hooks que se ejecutan cuando coincide el grupo
{
"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
}
]
}
]
}
}Notas:
descriptionson metadatos opcionales de nivel superior para un archivohooks.json. No cambian qué hooks se ejecutan.timeoutse expresa en segundos.- Si se omite
timeout, Codex usa600segundos para la mayoría de los hooks.SessionEndyInterruptusan1segundo de forma predeterminada y admiten hasta3segundos.
statusMessagees opcional.additionalContextLimitestablece cuántoadditionalContextpuede enviar un hook de comando al modelo antes de que Codex guarde el texto completo en el disco y envíe en su lugar una vista previa más breve. Consulta Salida extensa de hooks.commandWindowses una anulación opcional del comando solo para Windows. En TOML, usacommand_windowsocommandWindows.- Establece
asyncentruepara ejecutar un hook de comando en segundo plano. - Se admiten controladores
commandymcp_tool. Los controladorespromptyagentse analizan, pero se omiten. - Los comandos se ejecutan con el
cwdde la sesión como directorio de trabajo. - Para hooks locales del repositorio, es preferible resolver la ruta desde la raíz de git en lugar de usar una
ruta relativa como
.codex/hooks/.... Codex puede iniciarse desde un subdirectorio, y una ruta basada en la raíz de git mantiene estable la ubicación del hook.
TOML insertado equivalente en 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 de herramientas MCP
Un hook de herramienta MCP permite que un evento del ciclo de vida llame a una herramienta de un servidor MCP ya conectado. Envía argumentos estructurados directamente a la herramienta y utiliza el mismo contrato de revisión de confianza y salida que un hook de comandos.
Configurar un hook de herramienta MCP
Este hook solicita al servidor MCP scanner que analice cada parche después de que Codex escriba o
edite archivos:
{
"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"
}
]
}
]
}
}| Campo | Significado |
|---|---|
type |
Debe ser mcp_tool. |
server |
Nombre obligatorio de un servidor MCP ya conectado. |
tool |
Nombre obligatorio de una herramienta expuesta por ese servidor. |
input |
Objeto JSON opcional con plantillas de argumentos. El valor predeterminado es {}. |
timeout |
Tiempo de espera opcional de la ejecución activa, en segundos. El valor predeterminado es 600. |
statusMessage |
Mensaje opcional que se muestra mientras se ejecuta el hook. |
Expandir argumentos a partir del evento del hook
Usa ${field.nested} para leer un campo con notación de puntos del evento del hook. Un marcador de posición
que ocupe un valor completo conserva su tipo JSON. Un marcador de posición dentro de una cadena más larga
se representa como texto. Codex expande recursivamente los objetos y arrays.
Para un evento que contenga {"tool_input":{"file_path":"src/main.rs","count":3}},
esta plantilla de argumentos:
{
"path": "${tool_input.file_path}",
"count": "${tool_input.count}",
"message": "Scanning ${tool_input.file_path}"
}se convierte en:
{
"path": "src/main.rs",
"count": 3,
"message": "Scanning src/main.rs"
}Ejecución y ciclo de vida
- Los hooks utilizan una conexión MCP existente. No inician ni vuelven a conectar servidores.
- Un hook puede bloquear una operación cuando la herramienta devuelve una decisión de bloqueo. Los errores, los servidores ausentes y las herramientas no disponibles no bloquean la operación.
- Los hooks de herramientas MCP se ejecutan de forma síncrona. No solicitan aprobación para la herramienta ni activan otros hooks.
- Se aplica el tiempo de espera más corto del hook o del servidor. El tiempo dedicado a esperar una respuesta de elicitación MCP no cuenta para el tiempo de espera.
- Los hooks
SessionStartpueden ejecutarse antes de que un servidor MCP esté listo. Si ocurre, no bloquean la sesión. SessionEndno admite hooks de herramientas MCP.
Desactivar los hooks
Los hooks están habilitados de forma predeterminada. Para desactivarlos en config.toml, establece:
[features]
hooks = falseUsa hooks como clave canónica de la función. codex_hooks sigue funcionando como
alias obsoleto. Los administradores pueden forzar la desactivación de los hooks del mismo modo en
requirements.toml con [features].hooks = false.
Hooks administrados desde requirements.toml
Los requisitos administrados por la empresa también pueden definir hooks insertados bajo [hooks].
Esto resulta útil cuando los administradores quieren imponer la configuración de los hooks y, al mismo tiempo,
distribuir los scripts reales mediante MDM u otro sistema de administración de dispositivos.
Para imponer hooks administrados incluso a los usuarios que los hayan deshabilitado localmente, fija
[features].hooks = true en requirements.toml junto con [hooks]. Para ignorar
los hooks del usuario, proyecto, sesión y plugin sin dejar de permitir los hooks administrados por el
administrador, establece 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"Notas sobre los hooks administrados:
managed_dirse usa en macOS y Linux.windows_managed_dirse usa en Windows.- Codex no distribuye los scripts de
managed_dir; las herramientas de tu empresa deben instalarlos y actualizarlos por separado. - Los comandos de hooks administrados deben usar rutas absolutas de scripts dentro del directorio administrado configurado.
allow_managed_hooks_only = trueomite los hooks procedentes del usuario, proyecto, sesión y plugins, pero sigue cargando los hooks administrados desderequirements.tomly otras capas de configuración administradas.
Hooks incluidos en plugins
Cuando un plugin está habilitado, Codex puede cargar desde él hooks del ciclo de vida junto con los hooks del usuario, proyecto y administrados.
De forma predeterminada, Codex busca hooks/hooks.json dentro de la raíz del plugin. El manifiesto de un plugin
puede sustituir ese valor predeterminado mediante una entrada hooks en
.codex-plugin/plugin.json. La entrada del manifiesto puede ser una ruta con el prefijo ./, un
array de rutas con el prefijo ./, un objeto de hooks insertado o un array de objetos de hooks
insertados.
{
"name": "repo-policy",
"hooks": "./hooks/hooks.json"
}Las rutas de hooks del manifiesto se resuelven con respecto a la raíz del plugin y deben permanecer
dentro de ella. Si un manifiesto define hooks, Codex utiliza esas entradas del manifiesto
en lugar del valor predeterminado hooks/hooks.json.
Los comandos de hooks de plugins reciben estas variables de entorno:
PLUGIN_ROOTes una extensión específica de Codex que apunta a la raíz del plugin instalado.PLUGIN_DATAes una extensión específica de Codex que apunta al directorio de datos escribible del plugin.- Codex también establece
CLAUDE_PLUGIN_ROOTyCLAUDE_PLUGIN_DATApor compatibilidad con los hooks de plugins existentes.
Los hooks de plugins usan el mismo esquema de eventos que los demás hooks. Instalar o habilitar un plugin no hace que se confíe automáticamente en sus hooks; Codex omite los hooks incluidos en el plugin hasta que revises y marques como confiable la definición actual del hook.
Patrones de coincidencia
El campo matcher es una cadena de expresión regular que filtra cuándo se activan los hooks. Usa "*",
"" u omite matcher por completo para hacer coincidir todas las apariciones de un evento
admitido.
Solo algunos eventos actuales de Codex respetan matcher:
| Evento | Qué filtra matcher |
Notas |
|---|---|---|
PermissionRequest |
nombre de la herramienta | Se admiten Bash, apply_patch* y nombres de herramientas MCP |
PostToolUse |
nombre de la herramienta | Consulta Cobertura de herramientas |
PostCompact |
desencadenante de compactación | Los valores son manual o auto |
PreCompact |
desencadenante de compactación | Los valores son manual o auto |
PreToolUse |
nombre de la herramienta | Consulta Cobertura de herramientas |
SessionEnd |
motivo de finalización | Actualmente, solo other |
SessionStart |
origen del inicio | Los valores son startup, resume, clear y compact |
SubagentStart |
tipo de subagente | Los valores dependen del subagente que se inicia |
SubagentStop |
tipo de subagente | Los valores dependen del subagente que se detiene |
UserPromptSubmit |
no se admite | Cualquier matcher configurado se ignora para este evento |
Stop |
no se admite | Cualquier matcher configurado se ignora para este evento |
Interrupt |
no se admite | Cualquier matcher configurado se ignora para este evento |
*Para apply_patch, los valores matcher también pueden usar Edit o Write.
Ejemplos:
Bash^apply_patch$Edit|Writemcp__filesystem__read_filemcp__filesystem__.*startup|resume|clear|compactmanual|auto
Cobertura de herramientas
PreToolUse y PostToolUse pueden observar más que llamadas de shell y MCP. La mayoría de
las herramientas de funciones locales utilizan la misma ruta de hooks, por lo que puedes hacer coincidir el nombre de la herramienta,
inspeccionar sus argumentos JSON y, para PreToolUse, bloquear o reescribir la llamada.
| Ruta de la herramienta | PreToolUse |
PostToolUse |
Notas |
|---|---|---|---|
| Comandos de shell | Sí | Sí | Coinciden como Bash. |
Ejecución unificada (exec_command) |
Sí | Sí | Coincide como Bash. Un sondeo write_stdin posterior puede entregar el PostToolUse del comando original cuando finalice. |
apply_patch |
Sí | Sí | Coincide como apply_patch, Edit o Write. |
| Herramientas MCP | Sí | Sí | Haz coincidir el nombre de la herramienta MCP, como mcp__filesystem__read_file. |
| Otras herramientas de funciones locales | Sí | Sí | Haz coincidir el nombre de la herramienta de función, como update_plan. spawn_agent también coincide con Agent. |
Herramientas alojadas, como WebSearch |
No | No | No utilizan la ruta de hooks de herramientas de funciones locales. |
write_stdin es el transporte de una sesión de ejecución unificada existente. No vuelve a ejecutar
PreToolUse cuando envía entradas o sondea un comando que ya ha pasado
PreToolUse.
Algunas rutas de herramientas especializadas pueden excluirse de la ruta de hooks predeterminada. Considera los hooks de herramientas como una medida de protección útil, no como un límite de aplicación completo.
Campos de entrada comunes
Cada hook de comandos recibe un objeto JSON en stdin.
Estos son los campos compartidos que usarás habitualmente:
| Campo | Tipo | Significado |
|---|---|---|
session_id |
string |
Id. de la sesión actual de Codex. Los hooks de subagentes usan el id. de la sesión principal. |
transcript_path |
string | null |
Ruta al archivo de transcripción de la sesión, si existe |
cwd |
string |
Directorio de trabajo de la sesión |
hook_event_name |
string |
Nombre del evento de hook actual |
model |
string |
Extensión específica de Codex. Slug del modelo activo |
Los hooks con ámbito de turno incluyen turn_id como extensión específica de Codex en sus
tablas propias del evento.
SessionStart, PreToolUse, PermissionRequest, PostToolUse,
UserPromptSubmit, SubagentStart, SubagentStop, Stop y Interrupt también incluyen
permission_mode, que describe el modo de permisos actual como default,
acceptEdits, plan, dontAsk o bypassPermissions.
transcript_path apunta a una transcripción del chat por comodidad, pero el
formato de la transcripción no es una interfaz estable para los hooks y puede cambiar con el tiempo.
Si necesitas el formato de transmisión completo, consulta Esquemas.
Campos de salida comunes
SessionStart, PreCompact, PostCompact, UserPromptSubmit,
SubagentStop y Stop admiten estos campos JSON compartidos. SubagentStart
acepta la misma estructura para systemMessage y el contexto específico del hook, pero
continue: false no detiene al subagente:
{
"continue": true,
"stopReason": "optional",
"systemMessage": "optional",
"suppressOutput": false
}| Campo | Efecto |
|---|---|
continue |
Si es false, marca esa ejecución del hook como detenida |
stopReason |
Se registra como motivo de la detención |
systemMessage |
Se muestra como advertencia en la interfaz de usuario o el flujo de eventos |
suppressOutput |
Se analiza actualmente, pero aún no está implementado |
El código de salida 0 sin salida se considera correcto y Codex continúa.
PreToolUse y PermissionRequest admiten systemMessage, pero continue,
stopReason y suppressOutput no son compatibles actualmente con esos eventos.
Si un hook PreToolUse devuelve uno de esos campos no compatibles, Codex marca
la ejecución del hook como fallida, informa del error y continúa con la llamada a la herramienta.
PostToolUse admite systemMessage, continue: false y stopReason.
suppressOutput se analiza, pero actualmente no es compatible con ese evento.
Salida extensa de hooks
De forma predeterminada, Codex limita cada mensaje de salida de hook visible para el modelo a unos
2.500 tokens. Si un hook devuelve más, Codex guarda el texto completo en
<temp_dir>/hook_outputs/<session_id>/<uuid>.txt y proporciona al modelo una
vista previa del principio y el final junto con la ruta del archivo guardado. Este comportamiento se denomina
volcado: Codex almacena en el disco la salida sobredimensionada y la sustituye por una
vista previa más breve y visible para el modelo. Si no se puede escribir el archivo, el modelo sigue
recibiendo una vista previa truncada.
Para cualquier hook de comandos que devuelva additionalContext, establece
additionalContextLimit en el controlador para personalizar el umbral aproximado de
tokens:
{
"type": "command",
"command": "python3 ~/.codex/hooks/session_start.py",
"additionalContextLimit": 5000
}Omite additionalContextLimit para usar el umbral predeterminado de 2500 tokens. Usa un
entero positivo para seleccionar otro umbral, o 0 para transmitir directamente al modelo el
contexto adicional completo del controlador. Codex evalúa de forma independiente cada
controlador coincidente. Para los eventos que no pueden generar contexto adicional,
Codex ignora additionalContextLimit e informa de una advertencia de
configuración.
El ajuste solo se aplica a additionalContext. Los comentarios de herramientas y los prompts de continuación
conservan el límite predeterminado.
Como la salida sobredimensionada puede escribirse en el disco, evita devolver secretos u otros datos confidenciales en la salida del hook.
Ejecutar hooks en segundo plano
De forma predeterminada, Codex espera a que termine un hook de comandos antes de continuar con la
operación que lo activó. Establece async en true para ejecutar un hook de comandos en
segundo plano mientras Codex continúa.
Configurar un hook en segundo plano
Añade "async": true a un controlador de comandos en hooks.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.codex/hooks/post_tool_use.py",
"async": true,
"timeout": 120
}
]
}
]
}
}Para un hook insertado en config.toml, establece async = true:
[[hooks.PostToolUse]]
matcher = "Bash"
[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120Los hooks en segundo plano usan la misma entrada, matcher, revisión de confianza, tiempo de espera y
gestión de salidas extensas que los hooks de comando síncronos. Al igual
que con otros hooks de comando, timeout se mide en segundos y su valor predeterminado es
600. Los hooks Interrupt usan un valor predeterminado de un segundo y un máximo de tres segundos,
incluso cuando se ejecutan en segundo plano.
Cómo se ejecutan los hooks en segundo plano
Cuando termina un hook en segundo plano, Codex entrega la salida informativa admitida en el siguiente punto seguro de la conversación:
- Si hay un turno activo, Codex espera a que finalicen la solicitud actual al modelo y las llamadas a herramientas, y después pone la salida a disposición de la siguiente solicitud al modelo de ese turno.
- Si no hay ningún turno activo, Codex espera hasta el siguiente turno del usuario. La finalización de un hook en segundo plano no inicia un turno nuevo.
Usa la misma salida JSON específica del evento que en un hook síncrono. Codex añade
additionalContext al contexto del modelo y muestra systemMessage como
advertencia.
Limitaciones
- Codex ejecuta hasta ocho hooks en segundo plano simultáneamente por sesión. Los hooks adicionales esperan hasta que termine uno en ejecución.
- Cada invocación coincidente se ejecuta de forma independiente, y los hooks en segundo plano pueden terminar en un orden distinto del de inicio.
- Cuando termina la sesión, Codex cancela los hooks en segundo plano sin finalizar y descarta la salida que no se haya entregado.
- Los hooks
SessionEndsiempre se ejecutan de forma síncrona.
Hooks
SessionStart
matcher se aplica a source para este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
source |
string |
Cómo se inició la sesión: startup, resume, clear o compact |
El texto sin formato en stdout se añade como contexto adicional del desarrollador.
El JSON en stdout admite los campos de salida comunes y esta
estructura específica del hook:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Load the workspace conventions before editing."
}
}Ese texto additionalContext se añade como contexto adicional del desarrollador.
Después de que Codex compacte una sesión raíz, los hooks SessionStart que coincidan con
source: "compact" se ejecutan antes de la siguiente solicitud al modelo. Esto también se aplica cuando
se produce una compactación automática en medio de un turno: Codex entrega el contexto
adicional del hook a la continuación inmediata en lugar de esperar a un
turno posterior del usuario. Si el hook devuelve continue: false, Codex finaliza el turno
sin enviar otra solicitud al modelo.
SessionEnd
SessionEnd permite ejecutar un comando cuando termina una sesión, por ejemplo para guardar las notas
finales o limpiar archivos. Se ejecuta para el hilo principal cuando archivas o
eliminas una conversación que sigue abierta, cuando Codex se cierra con normalidad o después de que una
conversación haya permanecido inactiva y no esté abierta en ningún cliente conectado durante 30
minutos. No se ejecuta para subagentes.
Cambiar de conversación o llamar a thread/unsubscribe no termina
la sesión de inmediato, por lo que no ejecutará inmediatamente SessionEnd. El hook puede
seguir leyendo la transcripción de la sesión mientras se ejecuta.
matcher filtra reason para este evento. Por ahora, reason siempre es other.
Puedes omitir matcher o usar other para ejecutarlo en cada evento SessionEnd.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
reason |
string |
Por qué terminó la sesión: other |
Por ejemplo, un comando SessionEnd recibe:
{
"session_id": "thr_123",
"transcript_path": "/workspace/.codex/rollout.jsonl",
"cwd": "/workspace",
"hook_event_name": "SessionEnd",
"reason": "other"
}Los hooks SessionEnd siempre se ejecutan de forma síncrona, incluso cuando async es true. Son
informativos, por lo que su salida no dirigirá Codex ni mantendrá abierto el hilo. Si un
comando supera el tiempo de espera o termina con un error, Codex lo registra como un fallo del hook.
SubagentStart
matcher se aplica a agent_type para este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
agent_id |
string |
Identificador del subagente |
agent_type |
string |
Tipo o perfil del subagente |
permission_mode |
string |
Modo de permisos actual |
El texto sin formato en stdout se añade como contexto adicional del desarrollador para el subagente.
El JSON en stdout admite systemMessage y esta estructura específica del hook:
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Review the repository test conventions first."
}
}Ese texto additionalContext se añade como contexto adicional del desarrollador para el
subagente. continue: false se analiza por compatibilidad, pero no impide que el
subagente se inicie.
PreToolUse
PreToolUse puede interceptar Bash, las ediciones de archivos realizadas mediante apply_patch,
las llamadas a herramientas MCP y otras herramientas de funciones locales. Consulta Cobertura de
herramientas para conocer las rutas admitidas y las excepciones.
matcher se aplica a tool_name y a los alias de coincidencia. Para las ediciones de archivos mediante
apply_patch, los valores matcher pueden usar apply_patch, Edit o Write; la entrada del hook
sigue indicando tool_name: "apply_patch".
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
tool_name |
string |
Nombre canónico de la herramienta del hook, como Bash, apply_patch o un nombre MCP como mcp__fs__read |
tool_use_id |
string |
Id. de la llamada a la herramienta para esta invocación |
tool_input |
JSON value |
Entrada específica de la herramienta. Bash y apply_patch usan tool_input.command. MCP y otras herramientas de funciones locales envían sus argumentos. |
El texto sin formato en stdout se ignora.
El JSON en stdout puede usar systemMessage. Para rechazar una llamada admitida a una herramienta, devuelve
esta estructura específica del hook:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook."
}
}Codex también acepta esta estructura de bloqueo anterior:
{
"decision": "block",
"reason": "Destructive command blocked by hook."
}También puedes usar el código de salida 2 y escribir el motivo del bloqueo en stderr.
Para añadir contexto visible para el modelo sin bloquear, devuelve
hookSpecificOutput.additionalContext:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "The pending command touches generated files."
}
}Para reescribir una llamada admitida a una herramienta sin bloquearla, devuelve
permissionDecision: "allow" con updatedInput:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "echo rewritten"
}
}
}Para los comandos de Bash y apply_patch, updatedInput debe incluir un campo de cadena
command. Para MCP y otras herramientas de funciones locales, updatedInput es el
objeto de argumentos de sustitución. Devuelve updatedInput solo con
permissionDecision: "allow"; otras estructuras updatedInput se registran como
errores.
permissionDecision: "ask", el decision: "approve" heredado, continue: false,
stopReason y suppressOutput se analizan, pero aún no son compatibles. Codex marca
la ejecución del hook como fallida, informa del error y continúa con la llamada a la herramienta.
PermissionRequest
PermissionRequest se ejecuta cuando Codex está a punto de solicitar una aprobación, como una
escalación del shell o una aprobación de la red administrada. Puede permitir la solicitud, rechazarla
o abstenerse de decidir y dejar que continúe la solicitud normal de aprobación.
No se ejecuta para los comandos que no necesitan aprobación.
matcher se aplica a tool_name y a los alias de coincidencia. Los valores canónicos actuales
incluyen Bash, apply_patch y nombres de herramientas MCP como
mcp__server__tool; apply_patch también coincide con Edit y Write.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
tool_name |
string |
Nombre canónico de la herramienta del hook, como Bash, apply_patch o un nombre MCP como mcp__fs__read |
tool_input |
JSON value |
Entrada específica de la herramienta. Bash y apply_patch usan tool_input.command, mientras que las herramientas MCP envían todos los argumentos. |
tool_input.description |
string | null |
Motivo de aprobación legible para las personas, cuando Codex dispone de uno |
El texto sin formato en stdout se ignora.
Algunas entradas de herramientas pueden incluir una descripción legible para las personas, pero no debes depender de un
campo tool_input.description para todas las herramientas.
Para aprobar la solicitud, devuelve:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow"
}
}
}Para rechazar la solicitud, devuelve:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "deny",
"message": "Blocked by repository policy."
}
}
}Si varios hooks coincidentes devuelven decisiones, cualquier deny prevalece. De lo contrario, un
allow permite que la solicitud continúe sin mostrar el prompt de aprobación. Si ningún
hook coincidente toma una decisión, Codex utiliza el flujo normal de aprobación.
No devuelvas updatedInput, updatedPermissions ni interrupt para
PermissionRequest; esos campos están reservados para funciones futuras y actualmente provocan un
rechazo seguro.
PostToolUse
PostToolUse se ejecuta después de que las herramientas admitidas generen una salida, incluidas Bash,
apply_patch, las llamadas a herramientas MCP y otras herramientas de funciones locales. Para Bash, también
se ejecuta después de comandos que terminan con un estado distinto de cero. No puede deshacer los efectos
secundarios de una herramienta que ya se haya ejecutado. Consulta Cobertura de herramientas para
conocer las rutas admitidas y las excepciones.
matcher se aplica a tool_name y a los alias de coincidencia. Para las ediciones de archivos mediante
apply_patch, los valores matcher pueden usar apply_patch, Edit o Write; la entrada del hook
sigue indicando tool_name: "apply_patch".
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
tool_name |
string |
Nombre canónico de la herramienta del hook, como Bash, apply_patch o un nombre MCP como mcp__fs__read |
tool_use_id |
string |
Id. de la llamada a la herramienta para esta invocación |
tool_input |
JSON value |
Entrada específica de la herramienta. Bash y apply_patch usan tool_input.command. MCP y otras herramientas de funciones locales envían sus argumentos. |
tool_response |
JSON value |
Salida específica de la herramienta. Las herramientas MCP envían el resultado de la llamada MCP. Las demás herramientas de funciones locales suelen enviar su salida destinada al modelo. |
El texto sin formato en stdout se ignora.
El JSON en stdout puede usar systemMessage y esta estructura específica del hook:
{
"decision": "block",
"reason": "The Bash output needs review before continuing.",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "The command updated generated files."
}
}Ese texto additionalContext se añade como contexto adicional del desarrollador.
Para este evento, decision: "block" no deshace el comando de Bash completado.
En su lugar, Codex registra los comentarios, sustituye el resultado de la herramienta por esos
comentarios y permite que el modelo continúe desde el mensaje proporcionado por el hook.
También puedes usar el código de salida 2 y escribir el motivo de los comentarios en stderr.
Para detener el procesamiento normal del resultado original de la herramienta después de que el comando ya se haya
ejecutado, devuelve continue: false. Codex sustituirá el resultado de la herramienta por
tus comentarios o texto de detención y continuará desde allí.
updatedMCPToolOutput y suppressOutput se analizan, pero aún no son compatibles.
Codex marca la ejecución del hook como fallida, informa del error y continúa con el procesamiento
normal del resultado de la herramienta.
Llamadas a herramientas desde el modo de código
Cuando un modelo usa el modo de código para llamar a una herramienta desde JavaScript, las decisiones de los hooks se aplican
a esa llamada anidada. PreToolUse puede detener la herramienta antes de que se ejecute o reescribir
su entrada. Un PostToolUse de bloqueo no puede deshacer los efectos secundarios de la herramienta, pero sí
puede impedir que el resultado original llegue al script en ejecución.
| Resultado del hook | Qué ve el modo de código |
|---|---|
PreToolUse bloquea |
La promesa de la herramienta se rechaza antes de que esta se ejecute. |
PreToolUse devuelve updatedInput |
La herramienta se ejecuta con la entrada reescrita y la promesa se resuelve con ese resultado. |
PostToolUse devuelve decision: "block" o termina con el código 2 |
La herramienta se ejecuta y, después, la promesa se rechaza con el motivo del hook. |
PostToolUse devuelve continue: false |
Codex usa los comentarios del hook como resultado visible para el modelo, pero no rechaza la promesa anidada de la herramienta. |
PreCompact
PreCompact se ejecuta antes de que Codex compacte el chat. matcher se aplica
a trigger, cuyos valores son manual y auto.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
trigger |
string |
Qué activó la compactación: manual o auto |
El texto sin formato en stdout se ignora.
El JSON en stdout admite los campos de salida comunes. Si un
hook PreCompact coincidente devuelve continue: false, Codex se detiene antes de
compactar.
PostCompact
PostCompact se ejecuta después de que Codex compacte el chat. matcher se aplica
a trigger, cuyos valores son manual y auto.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
trigger |
string |
Qué activó la compactación: manual o auto |
El texto sin formato en stdout se ignora.
El JSON en stdout admite los campos de salida comunes. Si un
hook PostCompact coincidente devuelve continue: false, Codex se detiene después de
compactar.
UserPromptSubmit
matcher no se usa actualmente para este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
prompt |
string |
Prompt del usuario que está a punto de enviarse |
El texto sin formato en stdout se añade como contexto adicional del desarrollador.
El JSON en stdout admite los campos de salida comunes y
esta estructura específica del hook:
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Ask for a clearer reproduction before editing files."
}
}Ese texto additionalContext se añade como contexto adicional del desarrollador.
Para bloquear el prompt, devuelve:
{
"decision": "block",
"reason": "Ask for confirmation before doing that."
}También puedes usar el código de salida 2 y escribir el motivo del bloqueo en stderr.
SubagentStop
matcher se aplica a agent_type para este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
agent_id |
string |
Identificador del subagente |
agent_type |
string |
Tipo o perfil del subagente |
agent_transcript_path |
string | null |
Ruta al archivo de transcripción del subagente, si existe |
stop_hook_active |
boolean |
Indica si este subagente ya continuó |
last_assistant_message |
string | null |
Mensaje más reciente del asistente del subagente, si está disponible |
SubagentStop espera JSON en stdout cuando termina con 0. La salida de texto sin formato no es
válida para este evento.
El JSON en stdout admite los campos de salida comunes. Para pedir a
Codex que continúe el flujo del subagente, devuelve:
{
"decision": "block",
"reason": "Run one more focused pass inside the subagent."
}También puedes usar el código de salida 2 y escribir el motivo de la continuación en stderr.
Si algún hook SubagentStop coincidente devuelve continue: false, esto tiene
precedencia sobre las decisiones de continuación de otros hooks SubagentStop
coincidentes.
Stop
matcher no se usa actualmente para este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Id. del turno activo de Codex |
stop_hook_active |
boolean |
Indica si este turno ya continuó mediante Stop |
last_assistant_message |
string | null |
Texto del mensaje más reciente del asistente, si está disponible |
Stop espera JSON en stdout cuando termina con 0. La salida de texto sin formato no es válida
para este evento.
El JSON en stdout admite los campos de salida comunes. Para que
Codex continúe, devuelve:
{
"decision": "block",
"reason": "Run one more pass over the failing tests."
}También puedes usar el código de salida 2 y escribir el motivo de la continuación en stderr.
Para este evento, decision: "block" no rechaza el turno. En su lugar, indica a
Codex que continúe y crea automáticamente un nuevo prompt de continuación que actúa
como un nuevo prompt del usuario y usa tu reason como texto del prompt.
Si algún hook Stop coincidente devuelve continue: false, esto tiene precedencia
sobre las decisiones de continuación de otros hooks Stop coincidentes.
Interrupción
Interrupt se ejecuta cuando interrumpes un turno activo en el hilo principal. Úsalo
para registrar la interrupción o limpiar el trabajo iniciado por un hook. No se ejecuta
para hilos inactivos ni subagentes, y cualquier matcher configurado se ignora.
Además de los campos de entrada comunes, el evento incluye
turn_id, el id del turno interrumpido, y permission_mode.
El tiempo de espera predeterminado de los hooks de comando es de un segundo. Los tiempos de espera configurados están
limitados a entre uno y tres segundos. La salida del hook no puede impedir la
interrupción ni reiniciar el turno. Finaliza con 0 sin generar salida o devuelve JSON con
un systemMessage opcional para mostrar una advertencia. La salida de texto sin formato no es válida
para este evento.
{ "systemMessage": "Saved the interrupted turn to the local audit log." }Esquemas
Si necesitas el formato de transmisión actual exacto, consulta los esquemas generados en el repositorio de Codex en GitHub.
Alias de texto sin formato
- string | null