한국어

Codex 수명 주기 중에 결정론적 스크립트 실행

훅은 Codex를 위한 확장성 프레임워크입니다. 에이전트 루프 중에 스크립트나 MCP 도구를 실행하여 다음과 같은 기능을 구현할 수 있습니다.

  • 채팅을 사용자 지정 로깅/분석 엔진으로 전송
  • 팀의 프롬프트를 검사하여 실수로 API key를 붙여 넣는 것을 차단
  • 채팅을 요약하여 영구 메모리를 자동으로 생성
  • 채팅 턴이 중지될 때 사용자 지정 검증 검사를 실행하여 표준 적용
  • 특정 디렉터리에 있을 때 프롬프트 사용자 지정

유의해야 할 런타임 동작은 다음과 같습니다.

  • 여러 파일에서 일치하는 훅이 모두 실행됩니다.
  • 동일한 이벤트에 일치하는 여러 명령 훅은 동시에 시작되므로, 하나의 훅이 일치하는 다른 훅의 시작을 막을 수 없습니다.
  • 관리되지 않는 훅은 실행하기 전에 검토하고 신뢰해야 합니다.

훅은 대화의 여러 시점에 실행됩니다.

시점
턴 진행 중 PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop
활성 턴을 중단할 때 Interrupt(하위 에이전트에는 실행되지 않음)
세션 또는 하위 에이전트가 시작될 때 SessionStart, SubagentStart
기본 스레드가 종료될 때 SessionEnd(하위 에이전트에는 실행되지 않음)

Codex가 훅을 찾는 위치

Codex는 활성 구성 계층 옆에서 다음 형식 중 하나로 훅을 검색합니다.

  • hooks.json
  • config.toml 내부의 인라인 [hooks] 테이블

설치된 플러그인은 플러그인 매니페스트 또는 기본 hooks/hooks.json 파일을 통해 수명 주기 구성을 함께 제공할 수도 있습니다. 플러그인 패키징 규칙은 플러그인 빌드를 참조하세요.

실제로 가장 유용한 위치는 다음 네 곳입니다.

  • ~/.codex/hooks.json
  • ~/.codex/config.toml
  • <repo>/.codex/hooks.json
  • <repo>/.codex/config.toml

훅 소스가 둘 이상이면 Codex는 일치하는 모든 훅을 로드합니다. 우선순위가 높은 구성 계층이 우선순위가 낮은 계층의 훅을 대체하지는 않습니다. 한 계층에 hooks.json와 인라인 [hooks]가 모두 있으면 Codex는 둘을 병합하고 시작 시 경고를 표시합니다. 계층마다 하나의 표현 방식을 사용하세요.

Codex는 활성화된 플러그인에 포함된 훅도 검색할 수 있습니다. 플러그인에 포함된 훅은 다른 훅 소스와 함께 로드되며, 관리되지 않는 다른 훅과 동일한 신뢰 검토 절차를 사용합니다.

프로젝트 로컬 훅은 프로젝트의 .codex/ 계층을 신뢰하는 경우에만 로드됩니다. 신뢰하지 않는 프로젝트에서도 Codex는 자체 활성 구성 계층에 있는 사용자 및 시스템 훅을 계속 로드합니다.

훅 검토 및 신뢰

Codex는 실행 가능한 훅을 결정하기 전에 구성된 훅을 나열합니다. 관리되지 않는 훅을 실행하려면 먼저 정확한 훅 정의를 검토하고 신뢰해야 합니다. Codex는 훅의 현재 해시를 기준으로 신뢰를 기록하므로, 새 훅이나 변경된 훅은 검토 대상으로 표시되며 신뢰할 때까지 건너뜁니다.

CLI에서 /hooks을 사용하여 훅 소스를 검사하고, 새 훅이나 변경된 훅을 검토하고, 훅을 신뢰하거나 관리되지 않는 개별 훅을 비활성화하세요. 시작할 때 검토가 필요한 훅이 있으면 Codex는 /hooks을 열라는 경고를 표시합니다.

시스템, MDM, 클라우드 또는 requirements.toml 소스의 관리형 훅은 관리 대상으로 표시되고 정책에 따라 신뢰되며, 사용자 훅 브라우저에서 비활성화할 수 없습니다.

Codex 외부에서 훅 소스를 이미 검증하는 일회성 자동화에서는 --dangerously-bypass-hook-trust을 전달하여 해당 호출에 대해 저장된 훅 신뢰 없이 활성화된 훅을 실행하세요.

구성 구조

훅은 세 가지 수준으로 구성됩니다.

  • PreToolUse, PostToolUse, PreCompact, SubagentStart 또는 Stop 같은 훅 이벤트
  • 해당 이벤트의 일치 시점을 결정하는 매처 그룹
  • 매처 그룹이 일치할 때 실행되는 하나 이상의 훅 핸들러
{
  "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
          }
        ]
      }
    ]
  }
}

참고:

  • descriptionhooks.json 파일의 선택적 최상위 메타데이터입니다. 어떤 훅이 실행되는지는 변경하지 않습니다.
  • timeout의 단위는 초입니다.
  • timeout을 생략하면 Codex는 대부분의 훅에 600초를 사용합니다.
    • SessionEndInterrupt는 기본적으로 1초를 사용하며 최대 3초까지 지원합니다.
  • statusMessage는 선택 사항입니다.
  • additionalContextLimit은 명령 훅이 모델에 전송할 수 있는 additionalContext의 양을 설정합니다. 이 한도를 넘으면 Codex가 전체 텍스트를 디스크에 저장하고 대신 더 짧은 미리 보기를 전송합니다. 대용량 훅 출력을 참조하세요.
  • commandWindows는 Windows에서만 사용할 수 있는 선택적 명령 재정의입니다. TOML에서는 command_windows 또는 commandWindows를 사용합니다.
  • 명령 훅을 백그라운드에서 실행하려면 asynctrue로 설정합니다.
  • commandmcp_tool 핸들러가 지원됩니다. promptagent 핸들러는 구문 분석되지만 건너뜁니다.
  • 명령은 세션 cwd를 작업 디렉터리로 사용하여 실행됩니다.
  • 저장소 로컬 훅의 경우 .codex/hooks/... 같은 상대 경로를 사용하는 대신 git 루트를 기준으로 경로를 확인하는 것이 좋습니다. Codex가 하위 디렉터리에서 시작될 수 있으며, git 루트 기반 경로를 사용하면 훅 위치가 안정적으로 유지됩니다.

config.toml의 동등한 인라인 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"

MCP 도구 훅

MCP 도구 훅을 사용하면 수명 주기 이벤트가 이미 연결된 MCP 서버의 도구를 호출할 수 있습니다. 구조화된 인수를 도구에 직접 보내며 명령 훅과 동일한 신뢰 검토 및 출력 계약을 사용합니다.

MCP 도구 훅 구성

다음 훅은 Codex가 파일을 작성하거나 편집한 후 scanner MCP 서버에 각 패치를 검사하도록 요청합니다.

{
  "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"
          }
        ]
      }
    ]
  }
}
필드 의미
type mcp_tool이어야 합니다.
server 이미 연결된 MCP 서버의 필수 이름입니다.
tool 해당 서버가 노출하는 도구의 필수 이름입니다.
input 선택적 인수 템플릿 JSON 객체입니다. 기본값은 {}입니다.
timeout 선택적 활성 실행 제한 시간(초)입니다. 기본값은 600입니다.
statusMessage 훅 실행 중 표시되는 선택적 메시지입니다.

훅 이벤트에서 인수 확장

${field.nested}을 사용하여 훅 이벤트의 점 표기 필드를 읽습니다. 값 전체를 채우는 플레이스홀더는 JSON 타입을 유지합니다. 더 큰 문자열 내부의 플레이스홀더는 텍스트로 렌더링됩니다. Codex는 객체와 배열을 재귀적으로 확장합니다.

{"tool_input":{"file_path":"src/main.rs","count":3}}을 포함하는 이벤트에서 다음 인수 템플릿은

{
  "path": "${tool_input.file_path}",
  "count": "${tool_input.count}",
  "message": "Scanning ${tool_input.file_path}"
}

다음과 같이 변환됩니다.

{
  "path": "src/main.rs",
  "count": 3,
  "message": "Scanning src/main.rs"
}

실행 및 수명 주기

  • 훅은 기존 MCP 연결을 사용합니다. 서버를 시작하거나 다시 연결하지 않습니다.
  • 도구가 차단 결정을 반환하면 훅이 작업을 차단할 수 있습니다. 오류, 누락된 서버 및 사용할 수 없는 도구는 작업을 차단하지 않습니다.
  • MCP 도구 훅은 동기식으로 실행됩니다. 도구 승인을 요청하거나 다른 훅을 트리거하지 않습니다.
  • 훅과 서버 중 더 짧은 제한 시간이 적용됩니다. MCP 요청 응답을 기다리는 시간은 제한 시간에 포함되지 않습니다.
  • SessionStart 훅은 MCP 서버가 준비되기 전에 실행될 수 있습니다. 이 경우 세션을 차단하지 않습니다.
  • SessionEnd은 MCP 도구 훅을 지원하지 않습니다.

훅 끄기

훅은 기본적으로 활성화되어 있습니다. config.toml에서 끄려면 다음과 같이 설정합니다.

[features]
hooks = false

표준 기능 키로 hooks을 사용하세요. codex_hooks도 더 이상 권장되지 않는 별칭으로 계속 작동합니다. 관리자는 requirements.toml에서 [features].hooks = false을 사용해 같은 방식으로 훅을 강제로 끌 수 있습니다.

requirements.toml의 관리형 훅

기업 관리 요구 사항은 [hooks] 아래에 훅을 인라인으로 정의할 수도 있습니다. 관리자가 훅 구성을 강제하면서 실제 스크립트는 MDM이나 다른 기기 관리 시스템을 통해 배포하려는 경우 유용합니다. 로컬에서 훅을 비활성화한 사용자에게도 관리형 훅을 강제하려면 [hooks]과 함께 requirements.toml에서 [features].hooks = true을 고정하세요. 관리자 관리형 훅은 허용하면서 사용자, 프로젝트, 세션 및 플러그인 훅을 무시하려면 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"

관리형 훅 참고 사항:

  • managed_dir은 macOS와 Linux에서 사용됩니다.
  • windows_managed_dir는 Windows에서 사용됩니다.
  • Codex는 managed_dir의 스크립트를 배포하지 않습니다. 기업 도구를 사용하여 별도로 설치하고 업데이트해야 합니다.
  • 관리형 훅 명령은 구성된 관리 디렉터리 아래의 절대 스크립트 경로를 사용해야 합니다.
  • allow_managed_hooks_only = true은 사용자, 프로젝트, 세션 및 플러그인 소스의 훅을 건너뛰지만, requirements.toml 및 기타 관리형 구성 계층의 관리형 훅은 계속 로드합니다.

플러그인에 포함된 훅

플러그인이 활성화되면 Codex는 해당 플러그인의 수명 주기 훅을 사용자, 프로젝트 및 관리형 훅과 함께 로드할 수 있습니다.

기본적으로 Codex는 플러그인 루트 내부에서 hooks/hooks.json을 찾습니다. 플러그인 매니페스트의 .codex-plugin/plugin.jsonhooks 항목을 지정하여 이 기본값을 재정의할 수 있습니다. 매니페스트 항목은 ./ 접두사가 붙은 경로, ./ 접두사가 붙은 경로 배열, 인라인 훅 객체 또는 인라인 훅 객체 배열일 수 있습니다.

{
  "name": "repo-policy",
  "hooks": "./hooks/hooks.json"
}

매니페스트 훅 경로는 플러그인 루트를 기준으로 확인되며 해당 루트 내부에 있어야 합니다. 매니페스트가 hooks을 정의하면 Codex는 기본 hooks/hooks.json 대신 해당 매니페스트 항목을 사용합니다.

플러그인 훅 명령에는 다음 환경 변수가 제공됩니다.

  • PLUGIN_ROOT는 설치된 플러그인 루트를 가리키는 Codex 전용 확장입니다.
  • PLUGIN_DATA은 플러그인의 쓰기 가능한 데이터 디렉터리를 가리키는 Codex 전용 확장입니다.
  • Codex는 기존 플러그인 훅과의 호환성을 위해 CLAUDE_PLUGIN_ROOTCLAUDE_PLUGIN_DATA도 설정합니다.

플러그인 훅은 다른 훅과 동일한 이벤트 스키마를 사용합니다. 플러그인을 설치하거나 활성화해도 훅이 자동으로 신뢰되지는 않습니다. 현재 훅 정의를 검토하고 신뢰할 때까지 Codex는 플러그인에 포함된 훅을 건너뜁니다.

매처 패턴

matcher 필드는 훅 실행 시점을 필터링하는 정규식 문자열입니다. 지원되는 이벤트의 모든 발생과 일치시키려면 "*", ""을 사용하거나 matcher을 완전히 생략하세요.

현재 Codex 이벤트 중 일부만 matcher을 적용합니다.

이벤트 matcher의 필터링 대상 참고
PermissionRequest 도구 이름 지원 대상에는 Bash, apply_patch* 및 MCP 도구 이름이 포함됨
PostToolUse 도구 이름 도구 지원 범위 참조
PostCompact 압축 트리거 값은 manual 또는 auto
PreCompact 압축 트리거 값은 manual 또는 auto
PreToolUse 도구 이름 도구 지원 범위 참조
SessionEnd 종료 사유 현재는 other만 해당
SessionStart 시작 소스 값은 startup, resume, clearcompact
SubagentStart 하위 에이전트 유형 값은 시작되는 하위 에이전트에 따라 달라짐
SubagentStop 하위 에이전트 유형 값은 중지되는 하위 에이전트에 따라 달라짐
UserPromptSubmit 지원되지 않음 구성된 모든 matcher는 이 이벤트에서 무시됨
Stop 지원되지 않음 구성된 모든 matcher는 이 이벤트에서 무시됨
Interrupt 지원되지 않음 구성된 모든 matcher는 이 이벤트에서 무시됨

*apply_patch의 경우 matcher 값에 Edit 또는 Write도 사용할 수 있습니다.

예:

  • Bash
  • ^apply_patch$
  • Edit|Write
  • mcp__filesystem__read_file
  • mcp__filesystem__.*
  • startup|resume|clear|compact
  • manual|auto

도구 적용 범위

PreToolUsePostToolUse은 셸과 MCP 호출 외에도 더 많은 대상을 관찰할 수 있습니다. 대부분의 로컬 함수 도구는 동일한 훅 경로를 사용하므로 도구 이름을 일치시키고, JSON 인수를 검사하며, PreToolUse의 경우 호출을 차단하거나 다시 작성할 수 있습니다.

도구 경로 PreToolUse PostToolUse 참고
셸 명령 Bash으로 일치시킵니다.
통합 실행(exec_command) Bash으로 일치시킵니다. 이후 write_stdin 폴링은 해당 명령이 완료될 때 원래 명령의 PostToolUse을 전달할 수 있습니다.
apply_patch apply_patch, Edit 또는 Write으로 일치시킵니다.
MCP 도구 mcp__filesystem__read_file 같은 MCP 도구 이름을 일치시킵니다.
기타 로컬 함수 도구 update_plan 같은 함수 도구 이름을 일치시킵니다. spawn_agentAgent과도 일치합니다.
WebSearch 같은 호스팅 도구 아니요 아니요 로컬 함수 도구 훅 경로를 사용하지 않습니다.

write_stdin은 기존 통합 실행 세션을 위한 전송 수단입니다. 이미 PreToolUse을 통과한 명령에 입력을 보내거나 폴링할 때 PreToolUse을 다시 실행하지 않습니다.

일부 특수 도구 경로는 기본 훅 경로를 사용하지 않을 수 있습니다. 도구 훅은 유용한 보호 수단이지만 완전한 강제 적용 경계는 아닙니다.

공통 입력 필드

모든 명령 훅은 stdin에서 하나의 JSON 객체를 받습니다.

일반적으로 사용하는 공유 필드는 다음과 같습니다.

필드 타입 의미
session_id string 현재 Codex 세션 ID입니다. 하위 에이전트 훅은 상위 세션 ID를 사용합니다.
transcript_path string | null 세션 트랜스크립트 파일의 경로(있는 경우)
cwd string 세션의 작업 디렉터리
hook_event_name string 현재 훅 이벤트 이름
model string Codex 전용 확장입니다. 활성 모델 슬러그

턴 범위 훅의 이벤트별 표에는 turn_id이 Codex 전용 확장으로 나열됩니다.

SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop, StopInterrupt에는 현재 권한 모드를 default, acceptEdits, plan, dontAsk 또는 bypassPermissions으로 설명하는 permission_mode도 포함됩니다.

transcript_path은 편의를 위해 채팅 트랜스크립트를 가리키지만, 트랜스크립트 형식은 훅의 안정적인 인터페이스가 아니며 시간이 지나면서 변경될 수 있습니다.

전체 와이어 형식이 필요하면 스키마를 참조하세요.

공통 출력 필드

SessionStart, PreCompact, PostCompact, UserPromptSubmit, SubagentStopStop는 다음 공유 JSON 필드를 지원합니다. SubagentStartsystemMessage 및 훅별 컨텍스트에 동일한 구조를 허용하지만, continue: false은 하위 에이전트를 중지하지 않습니다.

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
필드 효과
continue false이면 해당 훅 실행을 중지된 것으로 표시
stopReason 중지 이유로 기록
systemMessage UI 또는 이벤트 스트림에 경고로 표시
suppressOutput 현재 파싱되지만 아직 구현되지 않음

출력 없이 종료 코드 0으로 끝나면 성공으로 처리되며 Codex가 계속 진행합니다.

PreToolUsePermissionRequestsystemMessage을 지원하지만, 현재 해당 이벤트에서는 continue, stopReasonsuppressOutput이 지원되지 않습니다. PreToolUse 훅이 지원되지 않는 필드 중 하나를 반환하면 Codex는 해당 훅 실행을 실패로 표시하고 오류를 보고한 후 도구 호출을 계속합니다.

PostToolUsesystemMessage, continue: falsestopReason를 지원합니다. suppressOutput는 파싱되지만 현재 해당 이벤트에서 지원되지 않습니다.

대용량 훅 출력

기본적으로 Codex는 모델에 표시되는 각 훅 출력 메시지를 약 2,500토큰으로 제한합니다. 훅이 더 많은 내용을 반환하면 Codex는 전체 텍스트를 <temp_dir>/hook_outputs/<session_id>/<uuid>.txt 아래에 저장하고, 저장된 파일 경로를 포함한 앞부분과 뒷부분의 미리 보기를 모델에 제공합니다. 이 동작을 스필링이라고 합니다. Codex는 지나치게 큰 출력을 디스크에 저장하고 모델에 표시되는 더 짧은 미리 보기로 대체합니다. 파일을 쓸 수 없는 경우에도 모델은 잘린 미리 보기를 받습니다.

additionalContext을 반환하는 모든 명령 훅에서는 핸들러의 additionalContextLimit을 설정하여 대략적인 토큰 임계값을 사용자 지정하세요.

{
  "type": "command",
  "command": "python3 ~/.codex/hooks/session_start.py",
  "additionalContextLimit": 5000
}

기본 2500토큰 임계값을 사용하려면 additionalContextLimit을 생략하세요. 다른 임계값을 선택하려면 양의 정수를 사용하고, 핸들러의 전체 추가 컨텍스트를 모델에 직접 전달하려면 0을 사용하세요. Codex는 일치하는 각 핸들러를 독립적으로 평가합니다. 추가 컨텍스트를 생성할 수 없는 이벤트에서는 Codex가 additionalContextLimit을 무시하고 구성 경고를 보고합니다.

이 설정은 additionalContext에만 적용됩니다. 도구 피드백과 계속 프롬프트는 기본 한도를 유지합니다.

지나치게 큰 출력은 디스크에 기록될 수 있으므로 훅 출력에 비밀이나 기타 민감한 데이터를 반환하지 마세요.

백그라운드에서 훅 실행

기본적으로 Codex는 명령 훅이 완료될 때까지 기다린 후 해당 훅을 트리거한 작업을 계속합니다. Codex가 계속 진행하는 동안 명령 훅을 백그라운드에서 실행하려면 asynctrue로 설정하세요.

백그라운드 훅 구성

hooks.json의 명령 핸들러에 "async": true을 추가합니다.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/post_tool_use.py",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}

config.toml의 인라인 훅에서는 async = true을 설정합니다.

[[hooks.PostToolUse]]
matcher = "Bash"

[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120

백그라운드 훅은 동기식 명령 훅과 동일한 입력, 매처, 신뢰 검토, 제한 시간 및 대용량 출력 처리 방식을 사용합니다. 다른 명령 훅과 마찬가지로 timeout의 단위는 초이며 기본값은 600입니다. Interrupt 훅은 백그라운드에서 실행되는 경우를 포함하여 기본값이 1초이고 최댓값이 3초입니다.

백그라운드 훅 실행 방식

백그라운드 훅이 완료되면 Codex는 대화에서 다음으로 안전한 시점에 지원되는 정보성 출력을 전달합니다.

  • 턴이 진행 중이면 Codex는 현재 모델 요청과 도구 호출이 완료될 때까지 기다린 다음, 해당 턴의 다음 모델 요청에서 출력을 사용할 수 있게 합니다.
  • 진행 중인 턴이 없으면 Codex는 다음 사용자 턴까지 기다립니다. 백그라운드 훅이 완료되어도 새 턴이 시작되지는 않습니다.

동기식 훅과 동일한 이벤트별 JSON 출력을 사용하세요. Codex는 모델의 컨텍스트에 additionalContext을 추가하고 systemMessage을 경고로 표시합니다.

제한 사항

  • Codex는 세션당 백그라운드 훅을 최대 8개까지 동시에 실행합니다. 추가 훅은 실행 중인 훅이 완료될 때까지 기다립니다.
  • 일치하는 각 호출은 독립적으로 실행되며, 백그라운드 훅은 시작된 순서와 다르게 완료될 수 있습니다.
  • 세션이 끝나면 Codex는 완료되지 않은 백그라운드 훅을 취소하고 아직 전달되지 않은 출력을 폐기합니다.
  • SessionEnd 훅은 항상 동기식으로 실행됩니다.

SessionStart

이 이벤트에서는 matchersource에 적용됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
source string 세션 시작 방식: startup, resume, clear 또는 compact

stdout의 일반 텍스트는 추가 개발자 컨텍스트로 추가됩니다.

stdout의 JSON은 공통 출력 필드와 다음과 같은 훅별 구조를 지원합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Load the workspace conventions before editing."
  }
}

해당 additionalContext 텍스트는 추가 개발자 컨텍스트로 추가됩니다.

Codex가 루트 세션을 압축한 후에는 source: "compact"과 일치하는 SessionStart 훅이 다음 모델 요청 전에 실행됩니다. 이는 턴 도중 자동 압축이 발생하는 경우에도 적용됩니다. Codex는 이후 사용자 턴까지 기다리지 않고 훅의 추가 컨텍스트를 바로 이어지는 요청에 전달합니다. 훅이 continue: false을 반환하면 Codex는 다른 모델 요청을 보내지 않고 턴을 종료합니다.

SessionEnd

SessionEnd을 사용하면 최종 메모리를 저장하거나 파일을 정리하는 등 세션이 끝날 때 명령을 실행할 수 있습니다. 아직 열려 있는 대화를 보관하거나 삭제할 때, Codex가 정상적으로 종료될 때 또는 대화가 30분 동안 유휴 상태이며 연결된 어떤 클라이언트에도 열려 있지 않을 때 기본 스레드에서 실행됩니다. 하위 에이전트에서는 실행되지 않습니다.

대화를 전환하거나 thread/unsubscribe을 호출해도 세션이 즉시 종료되지는 않으므로 SessionEnd이 바로 실행되지 않습니다. 훅이 실행되는 동안에도 세션 트랜스크립트를 읽을 수 있습니다.

이 이벤트에서 matcherreason을 필터링합니다. 현재 reason는 항상 other입니다. 모든 SessionEnd 이벤트에서 실행하려면 matcher을 생략하거나 other을 사용할 수 있습니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
reason string 세션 종료 이유: other

예를 들어 SessionEnd 명령은 다음을 받습니다.

{
  "session_id": "thr_123",
  "transcript_path": "/workspace/.codex/rollout.jsonl",
  "cwd": "/workspace",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

SessionEnd 훅은 asynctrue인 경우에도 항상 동기식으로 실행됩니다. 이 훅은 참고용이므로 출력이 Codex의 동작을 유도하거나 스레드를 열린 상태로 유지하지 않습니다. 명령이 시간 초과되거나 오류와 함께 종료되면 Codex는 훅 실패로 보고합니다.

SubagentStart

이 이벤트에서는 matcheragent_type에 적용됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
agent_id string 하위 에이전트 식별자
agent_type string 하위 에이전트 유형 또는 프로필
permission_mode string 현재 권한 모드

stdout의 일반 텍스트는 하위 에이전트의 추가 개발자 컨텍스트로 추가됩니다.

stdout의 JSON은 systemMessage과 다음과 같은 훅별 구조를 지원합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Review the repository test conventions first."
  }
}

해당 additionalContext 텍스트는 하위 에이전트의 추가 개발자 컨텍스트로 추가됩니다. continue: false은 호환성을 위해 파싱되지만 하위 에이전트의 시작을 막지는 않습니다.

PreToolUse

PreToolUse은 Bash, apply_patch을 통해 수행되는 파일 편집, MCP 도구 호출 및 기타 로컬 함수 도구를 가로챌 수 있습니다. 지원되는 경로와 예외는 도구 적용 범위를 참조하세요.

matchertool_name 및 매처 별칭에 적용됩니다. apply_patch를 통한 파일 편집의 경우 matcher 값으로 apply_patch, Edit 또는 Write를 사용할 수 있지만, 훅 입력에는 계속 tool_name: "apply_patch"이 보고됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
tool_name string Bash, apply_patch 또는 mcp__fs__read 같은 MCP 이름 등 표준 훅 도구 이름
tool_use_id string 이 호출의 도구 호출 ID
tool_input JSON value 도구별 입력입니다. Bashapply_patchtool_input.command을 사용합니다. MCP 및 기타 로컬 함수 도구는 인수를 보냅니다.

stdout의 일반 텍스트는 무시됩니다.

stdout의 JSON은 systemMessage을 사용할 수 있습니다. 지원되는 도구 호출을 거부하려면 다음과 같은 훅별 구조를 반환합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook."
  }
}

Codex는 다음과 같은 이전 차단 구조도 허용합니다.

{
  "decision": "block",
  "reason": "Destructive command blocked by hook."
}

종료 코드 2을 사용하고 차단 이유를 stderr에 쓸 수도 있습니다.

차단하지 않고 모델에 표시되는 컨텍스트를 추가하려면 hookSpecificOutput.additionalContext을 반환합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "additionalContext": "The pending command touches generated files."
  }
}

차단하지 않고 지원되는 도구 호출을 재작성하려면 updatedInput와 함께 permissionDecision: "allow"을 반환합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "echo rewritten"
    }
  }
}

Bash 명령과 apply_patch의 경우 updatedInput에 문자열 command 필드가 포함되어야 합니다. MCP 및 기타 로컬 함수 도구에서 updatedInput은 대체 인수 객체입니다. permissionDecision: "allow"과 함께 사용할 때만 updatedInput을 반환하세요. 다른 updatedInput 구조는 오류로 보고됩니다.

permissionDecision: "ask", 기존 decision: "approve", continue: false, stopReasonsuppressOutput는 파싱되지만 아직 지원되지 않습니다. Codex는 훅 실행을 실패로 표시하고 오류를 보고한 후 도구 호출을 계속합니다.

PermissionRequest

PermissionRequest는 셸 권한 상승이나 관리형 네트워크 승인처럼 Codex가 승인을 요청하기 직전에 실행됩니다. 요청을 허용하거나 거부할 수도 있고, 결정을 내리지 않고 일반적인 승인 프롬프트가 계속되게 할 수도 있습니다. 승인이 필요하지 않은 명령에는 실행되지 않습니다.

matchertool_name 및 매처 별칭에 적용됩니다. 현재 표준 값에는 Bash, apply_patchmcp__server__tool 같은 MCP 도구 이름이 포함됩니다. apply_patchEditWrite과도 일치합니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
tool_name string Bash, apply_patch 또는 mcp__fs__read 같은 MCP 이름 등 표준 훅 도구 이름
tool_input JSON value 도구별 입력입니다. Bashapply_patchtool_input.command을 사용하고 MCP 도구는 모든 인수를 보냅니다.
tool_input.description string | null Codex에 승인 이유가 있는 경우 사람이 읽을 수 있는 형태로 제공

stdout의 일반 텍스트는 무시됩니다.

일부 도구 입력에는 사람이 읽을 수 있는 설명이 포함될 수 있지만, 모든 도구에 tool_input.description 필드가 있다고 가정해서는 안 됩니다.

요청을 승인하려면 다음을 반환합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow"
    }
  }
}

요청을 거부하려면 다음을 반환합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "deny",
      "message": "Blocked by repository policy."
    }
  }
}

일치하는 여러 훅이 결정을 반환하면 deny이 항상 우선합니다. 그렇지 않으면 allow을 통해 승인 프롬프트를 표시하지 않고 요청을 계속할 수 있습니다. 일치하는 어떤 훅도 결정하지 않으면 Codex는 일반 승인 흐름을 사용합니다.

PermissionRequestupdatedInput, updatedPermissions 또는 interrupt를 반환하지 마세요. 해당 필드는 향후 동작을 위해 예약되어 있으며 현재는 안전을 위해 실패로 처리됩니다.

PostToolUse

PostToolUse은 Bash, apply_patch, MCP 도구 호출 및 기타 로컬 함수 도구를 포함하여 지원되는 도구가 출력을 생성한 후 실행됩니다. Bash의 경우 0이 아닌 상태로 종료된 명령 뒤에도 실행됩니다. 이미 실행된 도구의 부작용은 되돌릴 수 없습니다. 지원되는 경로와 예외는 도구 적용 범위를 참조하세요.

matchertool_name 및 매처 별칭에 적용됩니다. apply_patch를 통한 파일 편집의 경우 matcher 값으로 apply_patch, Edit 또는 Write를 사용할 수 있지만, 훅 입력에는 계속 tool_name: "apply_patch"이 보고됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
tool_name string Bash, apply_patch 또는 mcp__fs__read 같은 MCP 이름 등 표준 훅 도구 이름
tool_use_id string 이 호출의 도구 호출 ID
tool_input JSON value 도구별 입력입니다. Bashapply_patchtool_input.command을 사용합니다. MCP 및 기타 로컬 함수 도구는 인수를 보냅니다.
tool_response JSON value 도구별 출력입니다. MCP 도구는 MCP 호출 결과를 보냅니다. 기타 로컬 함수 도구는 일반적으로 모델에 표시되는 출력을 보냅니다.

stdout의 일반 텍스트는 무시됩니다.

stdout의 JSON은 systemMessage와 다음과 같은 훅별 구조를 사용할 수 있습니다.

{
  "decision": "block",
  "reason": "The Bash output needs review before continuing.",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "The command updated generated files."
  }
}

해당 additionalContext 텍스트는 추가 개발자 컨텍스트로 추가됩니다.

이 이벤트에서 decision: "block"은 완료된 Bash 명령을 되돌리지 않습니다. 대신 Codex는 피드백을 기록하고 도구 결과를 해당 피드백으로 대체한 후 훅이 제공한 메시지에서 모델을 계속 실행합니다.

종료 코드 2을 사용하고 피드백 이유를 stderr에 쓸 수도 있습니다.

명령이 이미 실행된 후 원래 도구 결과의 일반적인 처리를 중지하려면 continue: false을 반환하세요. Codex는 도구 결과를 사용자가 제공한 피드백 또는 중지 텍스트로 대체하고 그 지점부터 계속 진행합니다.

updatedMCPToolOutputsuppressOutput은 파싱되지만 아직 지원되지 않습니다. Codex는 훅 실행을 실패로 표시하고 오류를 보고한 후 도구 결과의 일반적인 처리를 계속합니다.

코드 모드의 도구 호출

모델이 코드 모드에서 JavaScript를 사용해 도구를 호출하면 훅 결정이 해당 중첩 호출에 적용됩니다. PreToolUse은 도구가 실행되기 전에 중지하거나 입력을 재작성할 수 있습니다. 차단하는 PostToolUse은 도구의 부작용을 되돌릴 수 없지만 원래 결과가 실행 중인 스크립트에 도달하는 것을 막을 수 있습니다.

훅 결과 코드 모드에 표시되는 결과
PreToolUse에서 차단 도구가 실행되기 전에 도구 프로미스가 거부됩니다.
PreToolUse에서 updatedInput 반환 도구가 재작성된 입력으로 실행되고 프로미스가 해당 결과로 이행됩니다.
PostToolUse에서 decision: "block" 반환 또는 종료 코드 2으로 종료 도구가 실행된 후 훅 이유와 함께 프로미스가 거부됩니다.
PostToolUse에서 continue: false 반환 Codex는 모델에 표시되는 결과에 훅 피드백을 사용하지만 중첩 도구 프로미스를 거부하지는 않습니다.

PreCompact

PreCompact는 Codex가 채팅을 압축하기 전에 실행됩니다. matcher은 값이 manualautotrigger에 적용됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
trigger string 압축을 트리거한 항목: manual 또는 auto

stdout의 일반 텍스트는 무시됩니다.

stdout의 JSON은 공통 출력 필드를 지원합니다. 일치하는 PreCompact 훅이 continue: false을 반환하면 Codex는 압축하기 전에 중지합니다.

PostCompact

PostCompact은 Codex가 채팅을 압축한 후 실행됩니다. matcher은 값이 manualautotrigger에 적용됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
trigger string 압축을 트리거한 항목: manual 또는 auto

stdout의 일반 텍스트는 무시됩니다.

stdout의 JSON은 공통 출력 필드를 지원합니다. 일치하는 PostCompact 훅이 continue: false을 반환하면 Codex는 압축 후 중지합니다.

UserPromptSubmit

현재 이 이벤트에는 matcher이 사용되지 않습니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
prompt string 전송 직전의 사용자 프롬프트

stdout의 일반 텍스트는 추가 개발자 컨텍스트로 추가됩니다.

stdout의 JSON은 공통 출력 필드와 다음과 같은 훅별 구조를 지원합니다.

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Ask for a clearer reproduction before editing files."
  }
}

해당 additionalContext 텍스트는 추가 개발자 컨텍스트로 추가됩니다.

프롬프트를 차단하려면 다음을 반환합니다.

{
  "decision": "block",
  "reason": "Ask for confirmation before doing that."
}

종료 코드 2을 사용하고 차단 이유를 stderr에 쓸 수도 있습니다.

SubagentStop

이 이벤트에서는 matcheragent_type에 적용됩니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
agent_id string 하위 에이전트 식별자
agent_type string 하위 에이전트 유형 또는 프로필
agent_transcript_path string | null 하위 에이전트 트랜스크립트 파일의 경로(있는 경우)
stop_hook_active boolean 이 하위 에이전트가 이미 계속되었는지 여부
last_assistant_message string | null 사용 가능한 경우 하위 에이전트의 최신 어시스턴트 메시지

SubagentStop0으로 종료될 때 stdout에 JSON이 있어야 합니다. 일반 텍스트 출력은 이 이벤트에 유효하지 않습니다.

stdout의 JSON은 공통 출력 필드를 지원합니다. Codex에 하위 에이전트 흐름을 계속하도록 요청하려면 다음을 반환합니다.

{
  "decision": "block",
  "reason": "Run one more focused pass inside the subagent."
}

종료 코드 2을 사용하고 계속 이유를 stderr에 쓸 수도 있습니다.

일치하는 SubagentStop 훅 중 하나라도 continue: false을 반환하면, 일치하는 다른 SubagentStop 훅의 계속 결정보다 우선합니다.

Stop

현재 이 이벤트에는 matcher이 사용되지 않습니다.

공통 입력 필드에 추가되는 필드:

필드 타입 의미
turn_id string Codex 전용 확장입니다. 활성 Codex 턴 ID
stop_hook_active boolean 이 턴이 이미 Stop에 의해 계속되었는지 여부
last_assistant_message string | null 사용 가능한 경우 최신 어시스턴트 메시지 텍스트

Stop0으로 종료될 때 stdout에 JSON이 있어야 합니다. 일반 텍스트 출력은 이 이벤트에 유효하지 않습니다.

stdout의 JSON은 공통 출력 필드를 지원합니다. Codex가 계속 진행하게 하려면 다음을 반환합니다.

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

종료 코드 2을 사용하고 계속 이유를 stderr에 쓸 수도 있습니다.

이 이벤트에서 decision: "block"은 턴을 거부하지 않습니다. 대신 Codex에 계속 진행하도록 지시하고, reason을 프롬프트 텍스트로 사용하는 새 계속 프롬프트를 자동으로 생성합니다. 이 프롬프트는 새로운 사용자 프롬프트로 작동합니다.

일치하는 Stop 훅 중 하나라도 continue: false을 반환하면 일치하는 다른 Stop 훅의 계속 결정보다 우선합니다.

중단

기본 스레드에서 활성 턴을 중단하면 Interrupt가 실행됩니다. 중단을 기록하거나 훅이 시작한 작업을 정리하는 데 사용합니다. 유휴 스레드나 하위 에이전트에는 실행되지 않으며, 구성된 모든 matcher는 무시됩니다.

공통 입력 필드 외에도 이 이벤트에는 중단된 턴의 ID인 turn_idpermission_mode가 포함됩니다.

명령 훅의 기본 제한 시간은 1초입니다. 구성 가능한 제한 시간은 1초에서 3초까지입니다. 훅 출력은 중단을 막거나 턴을 다시 시작할 수 없습니다. 출력 없이 0으로 종료하거나, 경고를 표시하기 위한 선택적 systemMessage가 포함된 JSON을 반환하세요. 일반 텍스트 출력은 이 이벤트에 유효하지 않습니다.

{ "systemMessage": "Saved the interrupted turn to the local audit log." }

스키마

정확한 현재 와이어 형식이 필요하면 Codex GitHub 저장소에서 생성된 스키마를 참조하세요.

일반 텍스트 별칭

  • string | null