Los hooks permiten extender las funciones de Codex. Con ellos puedes ejecutar scripts o herramientas MCP durante el ciclo de ejecución del agente para hacer tareas como estas:
- Enviar el chat a un motor personalizado de registro y análisis
- Revisar los prompts de tu equipo para impedir que se envíen claves de API pegadas por accidente
- Resumir chats para crear memorias persistentes de forma automática
- Ejecutar una validación personalizada cuando termina un turno del chat para exigir el cumplimiento de estándares
- Personalizar los prompts cuando trabajas en un directorio específico
Cómo se ejecutan los hooks:
- Se ejecutan todos los hooks que coincidan, aunque provengan de archivos distintos.
- Si varios hooks de comando coinciden con el mismo evento, se inician de forma concurrente. Ninguno puede impedir que se inicie otro hook que también coincida.
- Antes de ejecutar un hook no administrado, debes revisarlo y marcarlo como confiable.
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 |
| En cada intento de enviar una solicitud al modelo | PreModelRequest, PostModelRequest |
| Cuando interrumpes un turno activo | Interrupt (no se ejecuta para subagentes) |
| Cuando se inicia una sesión o un subagente | SessionStart, SubagentStart |
| Cuando termina el hilo principal | SessionEnd (no se ejecuta para subagentes) |
Dónde busca hooks Codex
Codex busca hooks en las ubicaciones de las capas de configuración activas. Pueden tener cualquiera de estas formas:
hooks.json- Tablas
[hooks]integradas enconfig.toml
Los complementos instalados también pueden incluir la configuración del ciclo de vida en su archivo de manifiesto o en el archivo predeterminado hooks/hooks.json. Consulta las reglas de empaquetado en Crear complementos.
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 hay más de una fuente de hooks, Codex carga todos los hooks que coincidan. Las capas de configuración de mayor prioridad no reemplazan los hooks de las capas de menor prioridad. Si una capa contiene tanto hooks.json como tablas [hooks] integradas, Codex los combina y muestra una advertencia al iniciarse. Procura usar una sola representación por capa.
Codex también puede detectar hooks incluidos en complementos habilitados. Estos hooks se cargan junto con los de otras fuentes y pasan por el mismo proceso de revisión de confianza que los demás hooks no administrados.
Los hooks locales de un proyecto solo se cargan si la capa .codex/ del proyecto es confiable. En proyectos que no son de confianza, Codex sigue cargando los hooks del usuario y del sistema desde sus respectivas capas de configuración activas.
Revisar hooks y marcarlos como confiables
Codex enumera los hooks configurados antes de determinar cuáles pueden ejecutarse. Para que un hook no administrado pueda ejecutarse, Codex requiere que revises su definición exacta y la marques como confiable. Codex asocia esa decisión con el hash actual del hook. Por eso, marca los hooks nuevos o modificados como pendientes de revisión y los omite hasta que los marques como confiables.
Usa /hooks en la CLI para inspeccionar las fuentes de los hooks, revisar hooks nuevos o modificados, marcarlos como confiables o deshabilitar hooks no administrados de forma individual. Si hay hooks pendientes de revisión al iniciar Codex, verás una advertencia que te indica que abras /hooks.
Los hooks administrados que provienen del sistema, de MDM, de la nube o de requirements.toml aparecen como administrados. La política los considera confiables y no puedes deshabilitarlos desde el explorador de hooks del usuario.
Para una automatización puntual que ya verifica las fuentes de los hooks fuera de Codex, pasa --dangerously-bypass-hook-trust. Así puedes ejecutar los hooks habilitados sin que esa invocación requiera una decisión de confianza guardada.
Estructura de la configuración
Los hooks se organizan en tres niveles:
- Un evento de hook, como
PreToolUse,PostToolUse,PreCompact,SubagentStartoStop - Un grupo de coincidencia que define los criterios que debe cumplir el evento
- Uno o más manejadores de hooks que se ejecutan cuando el evento cumple los criterios del 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:
descriptiones un campo opcional de metadatos en el nivel superior de un archivohooks.json. No cambia qué hooks se ejecutan.timeoutse expresa en segundos.- Si omites
timeout, Codex usa600segundos para la mayoría de los hooks.SessionEndeInterruptusan1segundo de forma predeterminada y admiten hasta3segundos.
statusMessagees opcional.additionalContextLimitestablece cuánto contenido deadditionalContextpuede enviar un hook de comando al modelo antes de que Codex guarde el texto completo en disco y envíe una vista previa más corta. Consulta Salidas extensas de los hooks.commandWindowspermite especificar un comando alternativo que se usa solo en Windows. En TOML, usacommand_windowsocommandWindows.- Establece
asyncentruepara ejecutar un hook de comando en segundo plano. - Se admiten los manejadores
commandymcp_tool. Los manejadorespromptyagentse interpretan, pero se omiten. - Los comandos usan el
cwdde la sesión como directorio de trabajo. - Para los hooks locales del repositorio, conviene resolver la ruta desde la raíz del repositorio de Git en lugar de usar una ruta relativa como
.codex/hooks/.... Codex puede iniciarse desde un subdirectorio. Una ruta basada en la raíz del repositorio permite localizar el hook independientemente del directorio de inicio.
Configuración TOML equivalente integrada 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 usa el mismo proceso de revisión de confianza y el mismo contrato de salida que un hook de comando.
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 que ofrece ese servidor. |
input |
Objeto JSON opcional con plantillas de argumentos. El valor predeterminado es {}. |
timeout |
Tiempo límite opcional de 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 del evento mediante la notación de puntos. Si un marcador de posición ocupa todo el valor, conserva su tipo JSON. Si forma parte de una cadena más larga, se convierte en texto. Codex expande los objetos y los arreglos de forma recursiva.
Para un evento que contiene {"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 usan una conexión MCP existente. No inician servidores ni vuelven a conectarlos.
- Un hook puede bloquear una operación si la herramienta devuelve una decisión de bloqueo. Los errores, la ausencia de un servidor y la falta de disponibilidad de una herramienta no bloquean la operación.
- Los hooks de herramientas MCP se ejecutan de forma síncrona. No solicitan aprobación para usar herramientas ni activan otros hooks.
- Se aplica el menor de los tiempos límite del hook y del servidor. El tiempo de espera de una respuesta a una solicitud de exploración de MCP no cuenta para ese límite.
- Los hooks
SessionStartpueden ejecutarse antes de que un servidor MCP esté listo. Si eso 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, usa esta configuración:
[features]
hooks = false
Usa hooks como clave de la función. codex_hooks aún funciona como alias obsoleto. Los administradores pueden desactivar los hooks de forma obligatoria en requirements.toml con [features].hooks = false.
Hooks administrados desde requirements.toml
Elige los hooks según el entorno que coordina la tarea.
Hooks con orquestación en la nube
En los espacios de trabajo que tienen habilitados tanto la política administrada como los hooks remotos, el modo Work en la nube con acceso a la computadora local y los dots pueden llamar a un servicio MCP cuando se producen determinados eventos de tareas o herramientas. Estas llamadas usan los manejadores mcp_tool que el administrador de tu espacio de trabajo configura en Seguridad de agentes. Ni el modo Work en la nube sin acceso a la computadora local ni las cuentas personales usan estos hooks empresariales.
La orquestación en la nube solo admite hooks MCP de alcance global, incluso cuando una herramienta se ejecuta en una computadora conectada. No admite manejadores de comandos, de prompts ni de agentes. Tampoco admite hooks de la configuración local, de complementos o de directorios locales, hooks específicos de un entorno ni hooks MCP de SessionEnd. Cuando tanto la orquestación como la ejecución son locales, los hilos exclusivamente locales del modo Work y de Codex siguen admitiendo los hooks que ya usaban.
Un hook compatible puede bloquear una acción si la deniega de forma explícita. Si se produce un error en PreToolUse, se agota el tiempo de espera o la respuesta tiene un formato incorrecto, es posible que la herramienta se ejecute. Los hooks MCP solo proporcionan una parte del registro de auditoría. Para saber qué comprobar durante la implementación y qué cubre la auditoría, consulta Comprobar la compatibilidad de los hooks y la red.
Hooks administrados en Codex local
Define los hooks locales de Codex en la sección [hooks] de requirements.toml. Para exigir la ejecución de los hooks administrados incluso si un usuario deshabilitó los hooks de forma local, fija [features].hooks = true en requirements.toml junto con [hooks]. Para omitir los hooks de usuario, proyecto, sesión y complementos y seguir permitiendo los que gestiona 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 los hooks administrados deben usar rutas absolutas a scripts dentro del directorio administrado que hayas configurado.
allow_managed_hooks_only = trueomite los hooks de usuario, proyecto, sesión y complementos, pero sigue cargando los hooks administrados derequirements.tomly de otras capas de configuración administradas.
Hooks incluidos en complementos
Cuando un complemento está habilitado, Codex puede cargar sus hooks de ciclo de vida junto con los hooks de usuario, de proyecto y administrados.
De forma predeterminada, Codex busca hooks/hooks.json dentro de la raíz del complemento. El archivo de manifiesto del complemento puede cambiar este comportamiento con una entrada hooks en .codex-plugin/plugin.json. La entrada del archivo de manifiesto puede ser una ruta que comience con ./, un arreglo de rutas que comiencen con ./, un objeto de hooks definido directamente en el archivo o un arreglo de esos objetos.
{
"name": "repo-policy",
"hooks": "./hooks/hooks.json"
}
Las rutas de los hooks del archivo de manifiesto se resuelven con respecto a la raíz del complemento y deben quedar dentro de ella. Si el archivo de manifiesto define hooks, Codex usa esas entradas en lugar del archivo predeterminado hooks/hooks.json.
Los comandos de los hooks de complementos reciben estas variables de entorno:
PLUGIN_ROOTes una extensión específica de Codex que apunta a la raíz del complemento instalado.PLUGIN_DATAes una extensión específica de Codex que apunta al directorio de datos del complemento con permisos de escritura.- Codex también establece
CLAUDE_PLUGIN_ROOTyCLAUDE_PLUGIN_DATApara mantener la compatibilidad con los hooks de complementos existentes.
Los hooks de complementos usan el mismo esquema de eventos que los demás hooks. Revísalos y márcalos como confiables antes de que se ejecuten. En la CLI, abre /hooks. En la aplicación de escritorio, selecciona Revisar hooks en el editor o abre Configuración > Hooks. Si una actualización del complemento modifica uno de los scripts incluidos, Codex exige una nueva revisión, aunque la definición del hook siga siendo la misma.
Patrones de coincidencia
El campo matcher contiene una expresión regular que determina cuándo se activa un hook. Usa "*" o "", u omite matcher por completo, para que coincida cada vez que ocurra un evento compatible.
Solo algunos eventos actuales de Codex tienen en cuenta matcher:
| Evento | Qué filtra matcher |
Notas |
|---|---|---|
PermissionRequest |
Nombre de la herramienta | Admite Bash, apply_patch* y nombres de herramientas MCP |
PostToolUse |
Nombre de la herramienta | Consulta Herramientas compatibles |
PostCompact |
Desencadenante de la compactación | Los valores son manual o auto |
PreModelRequest, PostModelRequest |
No se admite | Ambos eventos se ejecutan en cada intento de enviar una solicitud al modelo |
PreCompact |
Desencadenante de la compactación | Los valores son manual o auto |
PreToolUse |
Nombre de la herramienta | Consulta Herramientas compatibles |
SessionEnd |
Motivo de finalización | Por ahora, solo other |
SessionStart |
Origen del inicio | Los valores son startup, resume, clear, compact y fork |
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 | Se ignora cualquier matcher configurado para este evento |
Stop |
No se admite | Se ignora cualquier matcher configurado para este evento |
Interrupt |
No se admite | Se ignora cualquier matcher configurado para este evento |
*Para apply_patch, los valores de matcher también pueden ser Edit o Write.
Ejemplos:
Bash^apply_patch$Edit|Writemcp__filesystem__read_filemcp__filesystem__.*startup|resume|clear|compact|forkmanual|auto
Herramientas compatibles
PreToolUse y PostToolUse pueden observar llamadas a herramientas además de las llamadas al shell y a MCP. La mayoría de las herramientas de función locales usan el mismo mecanismo de hooks. Puedes filtrar por nombre de herramienta, inspeccionar sus argumentos JSON y, en el caso de PreToolUse, bloquear o reescribir la llamada.
| Vía de ejecución de la herramienta | PreToolUse |
PostToolUse |
Notas |
|---|---|---|---|
| Comandos de shell | Sí | Sí | Coinciden con Bash. |
Ejecución unificada (exec_command) |
Sí | Sí | Coincide con Bash. Una consulta posterior con write_stdin puede entregar el evento PostToolUse del comando original cuando este termine. |
apply_patch |
Sí | Sí | Coincide con apply_patch, Edit o Write. |
| Herramientas MCP | Sí | Sí | Usa el nombre de la herramienta MCP, como mcp__filesystem__read_file. |
| Otras herramientas de función locales | Sí | Sí | Usa el nombre de la herramienta de función, como update_plan. spawn_agent también coincide con Agent. |
Herramientas alojadas, como WebSearch |
No | No | Estas herramientas no usan el mecanismo de hooks de las herramientas de función locales. |
write_stdin sirve de transporte para una sesión de ejecución unificada existente. No vuelve a ejecutar PreToolUse cuando envía datos de entrada o consulta el estado de un comando que ya pasó por PreToolUse.
Algunas vías de ejecución especializadas pueden omitir el mecanismo de hooks predeterminado. Los hooks de herramientas ofrecen una protección útil, pero por sí solos no garantizan que se apliquen todas las restricciones.
Campos de entrada comunes
Cada hook de comando recibe un objeto JSON en stdin.
Estos son los campos compartidos que usarás habitualmente:
| Campo | Tipo | Significado |
|---|---|---|
session_id |
string |
Identificador de la sesión actual de Codex. Los hooks de subagentes usan el identificador 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 |
En los hooks cuyo alcance se limita a un turno, turn_id aparece como extensión específica de Codex en la tabla del evento correspondiente.
SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop, Stop e Interrupt también incluyen permission_mode, que indica el modo de permisos actual mediante uno de estos valores: default, acceptEdits, plan, dontAsk o bypassPermissions.
transcript_path apunta a la transcripción del chat para facilitar su consulta, 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 completo de los datos transmitidos, 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 |
Aparece como advertencia en la interfaz de usuario o en el flujo de eventos |
suppressOutput |
El campo se interpreta, pero su efecto aún no está implementado |
El código de salida 0 sin datos de salida indica que la ejecución tuvo éxito y Codex continúa.
PreToolUse y PermissionRequest admiten systemMessage, pero estos eventos aún no admiten continue, stopReason ni suppressOutput. Si un hook PreToolUse devuelve uno de esos campos no admitidos, 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. El campo suppressOutput se interpreta, pero este evento aún no admite su uso.
Salidas extensas de los hooks
De forma predeterminada, Codex limita cada mensaje de salida de un hook visible para el modelo a unos 2500 tokens. Si un hook devuelve más, Codex guarda el texto completo en <temp_dir>/hook_outputs/<session_id>/<uuid>.txt y entrega al modelo una vista previa del principio y del final junto con la ruta del archivo guardado. Este comportamiento se denomina volcado a disco: Codex guarda en disco la salida que excede el límite y la sustituye por una vista previa más breve que el modelo puede ver. Si no se puede escribir el archivo, el modelo recibe de todos modos una vista previa truncada.
Mantén breve el contexto de los hooks y los complementos. El contexto de varios hooks y complementos se acumula y puede reducir el rendimiento del modelo. Aumentar additionalContextLimit incrementa ese riesgo. Evita establecer el límite en 0 a menos que el hook imponga un límite estricto a su salida. De lo contrario, un solo hook puede ocupar toda la ventana de contexto.
Para personalizar el umbral aproximado de tokens de un hook de comando que devuelve additionalContext, establece additionalContextLimit en su manejador:
{
"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 elegir otro umbral, o 0 para pasar todo el contexto adicional del manejador directamente al modelo. Codex evalúa por separado cada manejador que coincida. En los eventos que no pueden generar contexto adicional, Codex ignora additionalContextLimit y muestra una advertencia de configuración.
Esta configuración solo se aplica a additionalContext. Los comentarios sobre las herramientas y los prompts de continuación conservan el límite predeterminado.
Como las salidas extensas pueden guardarse en disco, evita devolver secretos u otros datos confidenciales en la salida de los hooks.
Ejecutar hooks en segundo plano
De forma predeterminada, Codex espera a que termine el hook de comando antes de continuar con la operación que lo activó. Establece async en true para ejecutar el hook de comando en segundo plano mientras Codex continúa.
Configurar un hook en segundo plano
Agrega "async": true a un manejador 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 definido directamente 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 = 120
Los hooks en segundo plano usan los mismos datos de entrada, criterios de coincidencia, proceso de revisión de confianza, tiempo límite y tratamiento de salidas extensas que los hooks de comando síncronos. Como en los demás hooks de comando, timeout se mide en segundos y su valor predeterminado es 600. Los hooks Interrupt tienen un tiempo límite predeterminado de un segundo y admiten 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 momento seguro de la conversación:
- Si hay un turno activo, Codex espera a que terminen la solicitud actual al modelo y las llamadas a herramientas. Luego pone la salida a disposición de la siguiente solicitud al modelo dentro de ese turno.
- Si no hay un 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 usarías con un hook síncrono. Codex agrega additionalContext al contexto del modelo y muestra systemMessage como advertencia.
Los hooks en segundo plano no pueden bloquear, aprobar, reescribir ni controlar de otro modo la operación que los activó. Usa hooks síncronos para aplicar políticas de herramientas, tomar decisiones sobre permisos, rechazar prompts o continuar un turno.
Limitaciones
- Codex ejecuta hasta ocho hooks en segundo plano de forma concurrente por sesión. Los demás esperan a que termine uno de los que están en ejecución.
- Cada invocación que coincide se ejecuta de forma independiente. Los hooks en segundo plano pueden terminar en un orden distinto al de inicio.
- Al terminar la sesión, Codex cancela los hooks en segundo plano que aún se estén ejecutando y descarta la salida que todavía no haya entregado.
- Los hooks
SessionEndsiempre se ejecutan de forma síncrona.
Hooks
SessionStart
En este evento, matcher se aplica a source.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
source |
string |
Cómo se inició la sesión: startup, resume, clear, compact o fork |
Un hilo creado mediante un fork usa source: "fork". Al reanudar un hilo con un historial proporcionado, se sigue usando source: "resume".
El texto plano de stdout se agrega como contexto adicional del desarrollador.
El JSON de stdout admite los campos de salida comunes y esta estructura específica del hook:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Load the workspace conventions before editing."
}
}
El texto de additionalContext se agrega como contexto adicional del desarrollador.
Después de que Codex compacta una sesión raíz, los hooks SessionStart que coinciden con source: "compact" se ejecutan antes de la siguiente solicitud al modelo. Esto también ocurre si la compactación automática tiene lugar en medio de un turno. Codex entrega el contexto adicional del hook en cuanto continúa el turno, sin esperar a un turno posterior del usuario. Si el hook devuelve continue: false, Codex termina el turno sin enviar otra solicitud al modelo.
SessionEnd
SessionEnd te permite ejecutar un comando cuando termina una sesión, por ejemplo, para guardar notas finales o limpiar archivos. Se ejecuta en el hilo principal cuando archivas o eliminas una conversación que sigue abierta, cuando Codex se cierra normalmente o cuando una conversación lleva 30 minutos inactiva sin estar abierta en ningún cliente conectado. No se ejecuta para subagentes.
Cambiar de conversación o llamar a thread/unsubscribe no termina la sesión de inmediato, por lo que tampoco se ejecuta SessionEnd en ese momento. El hook puede seguir leyendo la transcripción de la sesión mientras se ejecuta.
En este evento, matcher filtra por reason. Por ahora, reason siempre es other. Puedes omitir matcher o usar other para que el hook se ejecute en cada evento SessionEnd.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
reason |
string |
Motivo por el que terminó la sesión: other |
Por ejemplo, un comando de 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 si async es true. Su salida es informativa, por lo que no modifica el comportamiento de Codex ni mantiene abierto el hilo. Si un comando excede el tiempo límite o termina con un error, Codex lo informa como una falla del hook.
SubagentStart
En este evento, matcher se aplica a agent_type.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Identificador 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 plano de stdout se agrega como contexto adicional del desarrollador para el subagente.
El JSON de stdout admite systemMessage y esta estructura específica del hook:
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Review the repository test conventions first."
}
}
El texto de additionalContext se agrega como contexto adicional del desarrollador para el subagente. El valor continue: false se interpreta por compatibilidad, pero no impide que se inicie el subagente.
PreToolUse
PreToolUse puede interceptar comandos de Bash, ediciones de archivos realizadas mediante apply_patch, llamadas a herramientas MCP y llamadas a otras herramientas de función locales. Consulta las vías de ejecución compatibles y sus excepciones en Herramientas compatibles.
matcher se aplica a tool_name y a los alias de coincidencia. Para las ediciones de archivos mediante apply_patch, los valores de matcher pueden ser 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. Identificador del turno activo de Codex |
tool_name |
string |
Nombre canónico de la herramienta para el hook, como Bash, apply_patch o un nombre de MCP como mcp__fs__read |
tool_use_id |
string |
Identificador de la llamada a la herramienta en esta invocación |
tool_input |
JSON value |
Entrada específica de la herramienta. Bash y apply_patch usan tool_input.command. Las herramientas MCP y otras herramientas de función locales envían sus argumentos. |
El texto plano de stdout se ignora.
El JSON de stdout puede usar systemMessage. Para denegar una llamada a una herramienta compatible, 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 agregar contexto visible para el modelo sin bloquear la llamada, devuelve hookSpecificOutput.additionalContext:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": "The pending command touches generated files."
}
}
Para reescribir una llamada a una herramienta compatible sin bloquearla, devuelve permissionDecision: "allow" junto con updatedInput:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"command": "echo rewritten"
}
}
}
Para comandos de Bash y apply_patch, updatedInput debe incluir un campo command de tipo cadena. Para MCP y otras herramientas de función locales, updatedInput es el objeto de argumentos que reemplaza al original. Devuelve updatedInput solo junto con permissionDecision: "allow". Codex informa de un error si updatedInput tiene otra estructura.
Los valores permissionDecision: "ask", decision: "approve" de versiones anteriores, continue: false, stopReason y suppressOutput se interpretan, pero aún no se admiten. 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á por solicitar aprobación, por ejemplo, para elevar los permisos de un comando de shell o autorizar el acceso a una red administrada. Puede permitir la solicitud, denegarla o abstenerse de decidir para que se muestre la solicitud de aprobación habitual. No se ejecuta para comandos que no requieren 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. La herramienta 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. Identificador del turno activo de Codex |
tool_name |
string |
Nombre canónico de la herramienta para el hook, como Bash, apply_patch o un nombre de 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 sus argumentos. |
tool_input.description |
string | null |
Motivo de la solicitud de aprobación en lenguaje natural, si Codex dispone de él |
El texto plano de stdout se ignora.
La entrada de algunas herramientas puede incluir una descripción en lenguaje natural, pero no cuentes con que todas tengan el campo tool_input.description.
Para aprobar la solicitud, devuelve:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow"
}
}
}
Para denegar la solicitud, devuelve:
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "deny",
"message": "Blocked by repository policy."
}
}
}
Si varios hooks coinciden y devuelven decisiones, cualquier deny tiene prioridad. En los demás casos, un allow permite que la solicitud continúe sin mostrar el mensaje de aprobación. Si ningún hook coincidente toma una decisión, Codex usa el flujo de aprobación habitual.
No devuelvas updatedInput, updatedPermissions ni interrupt para PermissionRequest. Estos campos están reservados para funciones futuras y, por ahora, su uso impide que se autorice la solicitud.
PostToolUse
PostToolUse se ejecuta después de que las herramientas compatibles generan una salida. Esto incluye Bash, apply_patch, las llamadas a herramientas MCP y otras herramientas de función locales. En Bash, también se ejecuta después de comandos que terminan con un código de salida distinto de cero. No puede revertir los efectos de una herramienta que ya se ejecutó. Consulta las vías de ejecución compatibles y sus excepciones en Herramientas compatibles.
matcher se aplica a tool_name y a los alias de coincidencia. Para las ediciones de archivos mediante apply_patch, los valores de matcher pueden ser 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. Identificador del turno activo de Codex |
tool_name |
string |
Nombre canónico de la herramienta para el hook, como Bash, apply_patch o un nombre de MCP como mcp__fs__read |
tool_use_id |
string |
Identificador de la llamada a la herramienta en esta invocación |
tool_input |
JSON value |
Entrada específica de la herramienta. Bash y apply_patch usan tool_input.command. Las herramientas MCP y otras herramientas de función 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 función locales normalmente envían la salida que recibe el modelo. |
El texto plano de stdout se ignora.
El JSON de 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."
}
}
El texto de additionalContext se agrega como contexto adicional del desarrollador.
En este evento, decision: "block" no revierte el comando de Bash que ya terminó. Codex registra los comentarios del hook, sustituye el resultado de la herramienta por esos comentarios y reanuda la ejecución del modelo a partir del mensaje del hook.
También puedes usar el código de salida 2 y escribir el motivo en stderr como comentario.
Para detener el procesamiento normal del resultado original después de que se haya ejecutado el comando, devuelve continue: false. Codex reemplazará el resultado de la herramienta por tus comentarios o por el texto que indica el motivo de la detención y continuará a partir de ahí.
Los campos updatedMCPToolOutput y suppressOutput se interpretan, pero aún no se admiten. 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 bloqueo de PostToolUse no puede revertir los efectos de la herramienta, pero sí puede impedir que el script en ejecución reciba el resultado original.
| Resultado del hook | Qué ocurre en el modo de código |
|---|---|
PreToolUse bloquea la llamada |
La promesa de la herramienta se rechaza antes de que se ejecute la herramienta. |
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 luego la promesa se rechaza con el motivo que indicó el hook. |
PostToolUse devuelve continue: false |
Codex usa los comentarios del hook como resultado visible para el modelo, pero no rechaza la promesa de la llamada anidada a la herramienta. |
PreModelRequest y PostModelRequest
Usa estos hooks para registrar las solicitudes al modelo, incluidos los reintentos dentro de un mismo turno. PreModelRequest se ejecuta antes de cada intento. PostModelRequest informa del resultado. Ambos reciben el modelo y turn_id, además de agent_id y agent_type cuando están disponibles. La salida de estos hooks solo sirve para fines de diagnóstico. No puede modificar el contexto del modelo ni bloquear una solicitud.
PostModelRequest incluye tres campos adicionales:
| Campo | Significado |
|---|---|
status |
completed, failed o cancelled. |
error |
Mensaje de error si está disponible. De lo contrario, null. |
token_usage |
Recuentos de tokens del intento si están disponibles. De lo contrario, null. |
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. Identificador del turno activo de Codex |
trigger |
string |
Qué activó la compactación: manual o auto |
El texto plano de stdout se ignora.
El JSON de stdout admite los campos de salida comunes. Si un hook PreCompact que coincide devuelve continue: false, Codex se detiene antes de la compactación.
PostCompact
PostCompact se ejecuta después de que Codex compacta 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. Identificador del turno activo de Codex |
trigger |
string |
Qué activó la compactación: manual o auto |
El texto plano de stdout se ignora.
El JSON de stdout admite los campos de salida comunes. Si un hook PostCompact que coincide devuelve continue: false, Codex se detiene después de la compactación.
UserPromptSubmit
Por ahora, matcher no se usa en este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Identificador del turno activo de Codex |
prompt |
string |
Prompt del usuario que está a punto de enviarse |
El texto plano de stdout se agrega como contexto adicional del desarrollador.
El JSON de 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."
}
}
El texto de additionalContext se agrega 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
En este evento, matcher se aplica a agent_type.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Identificador 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 ya se hizo que este subagente continuara |
last_assistant_message |
string | null |
Último mensaje del asistente del subagente, si está disponible |
SubagentStop espera JSON en stdout cuando termina con el código 0. Este evento no admite texto plano como salida.
El JSON de stdout admite los campos de salida comunes. Para pedirle 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 alguno de los hooks SubagentStop que coinciden devuelve continue: false, ese valor tiene prioridad sobre las decisiones de continuar de otros hooks SubagentStop que también coincidan.
Stop
Por ahora, matcher no se usa en este evento.
Campos adicionales a los campos de entrada comunes:
| Campo | Tipo | Significado |
|---|---|---|
turn_id |
string |
Extensión específica de Codex. Identificador del turno activo de Codex |
stop_hook_active |
boolean |
Indica si ya se prolongó este turno mediante Stop |
last_assistant_message |
string | null |
Texto del último mensaje del asistente, si está disponible |
Stop espera JSON en stdout cuando termina con el código 0. Este evento no admite texto plano como salida.
El JSON de 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.
En este evento, decision: "block" no rechaza el turno. Le indica a Codex que continúe y crea automáticamente un prompt de continuación que funciona como un nuevo prompt del usuario. El texto de ese prompt es el valor de reason que proporcionaste.
Si alguno de los hooks Stop que coinciden devuelve continue: false, ese valor tiene prioridad sobre las decisiones de continuar de otros hooks Stop que también coincidan.
Interrupt
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 en hilos inactivos ni para subagentes, y se ignora cualquier matcher configurado.
Además de los campos de entrada comunes, el evento incluye turn_id, que identifica el turno interrumpido, y permission_mode.
Los hooks de comando tienen un tiempo límite predeterminado de un segundo. Puedes configurar un tiempo límite de entre uno y tres segundos. La salida del hook no puede impedir la interrupción ni reiniciar el turno. Termina con el código 0 sin generar salida, o devuelve JSON con un campo opcional systemMessage para mostrar una advertencia. Este evento no admite texto plano como salida.
{ "systemMessage": "Saved the interrupted turn to the local audit log." }
Esquemas
Los esquemas de la rama main que se enlazan a continuación pueden incluir campos de hooks que no están disponibles en la versión actual. Usa esta página como referencia para el comportamiento de la versión publicada.
Si necesitas el formato exacto que se usa actualmente para transmitir los datos, consulta los esquemas generados en el repositorio de Codex en GitHub.