Tiếng Việt

Hook

Hook

Chạy các tập lệnh có tính xác định trong suốt vòng đời Codex

Hook là một khung mở rộng dành cho Codex. Chúng cho phép bạn chạy tập lệnh hoặc công cụ MCP trong vòng lặp tác nhân, hỗ trợ các tính năng như:

  • Gửi cuộc trò chuyện đến một công cụ ghi nhật ký/phân tích tùy chỉnh
  • Quét prompt của nhóm để ngăn việc vô tình dán API key
  • Tóm tắt cuộc trò chuyện để tự động tạo bộ nhớ lâu dài
  • Chạy bước kiểm tra xác thực tùy chỉnh khi một lượt trò chuyện dừng lại nhằm thực thi các tiêu chuẩn
  • Tùy chỉnh prompt khi ở trong một thư mục nhất định

Hành vi trong thời gian chạy cần lưu ý:

  • Tất cả hook khớp từ nhiều tệp đều chạy.
  • Nhiều hook lệnh khớp với cùng một sự kiện được khởi chạy đồng thời, vì vậy một hook không thể ngăn một hook khớp khác bắt đầu.
  • Bạn phải xem xét và tin cậy các hook không được quản lý trước khi chúng chạy.

Hook chạy tại các thời điểm khác nhau trong cuộc trò chuyện:

Thời điểm Hook
Trong một lượt PreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop
Khi bạn ngắt một lượt đang hoạt động Interrupt (không chạy cho tác nhân phụ)
Khi một phiên hoặc tác nhân phụ bắt đầu SessionStart, SubagentStart
Khi luồng chính kết thúc SessionEnd (không chạy cho tác nhân phụ)

Nơi Codex tìm hook

Codex phát hiện hook bên cạnh các lớp cấu hình đang hoạt động ở một trong hai dạng sau:

  • hooks.json
  • các bảng [hooks] nội tuyến bên trong config.toml

Plugin đã cài đặt cũng có thể đóng gói cấu hình vòng đời thông qua manifest của plugin hoặc tệp hooks/hooks.json mặc định. Xem Xây dựng plugin để biết các quy tắc đóng gói plugin.

Trong thực tế, bốn vị trí hữu ích nhất là:

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

Nếu có nhiều hơn một nguồn hook, Codex sẽ tải tất cả hook khớp. Các lớp cấu hình có độ ưu tiên cao hơn không thay thế hook ở lớp có độ ưu tiên thấp hơn. Nếu một lớp chứa cả hooks.json[hooks] nội tuyến, Codex sẽ hợp nhất chúng và cảnh báo khi khởi động. Mỗi lớp nên chỉ dùng một cách biểu diễn.

Codex cũng có thể phát hiện hook được đóng gói cùng các plugin đã bật. Hook đi kèm plugin được tải cùng các nguồn hook khác và sử dụng cùng quy trình xem xét độ tin cậy như các hook không được quản lý khác.

Hook cục bộ của dự án chỉ được tải khi lớp .codex/ của dự án được tin cậy. Trong các dự án không được tin cậy, Codex vẫn tải hook người dùng và hệ thống từ các lớp cấu hình đang hoạt động tương ứng.

Xem xét và tin cậy hook

Codex liệt kê các hook đã cấu hình trước khi quyết định hook nào có thể chạy. Trước khi một hook không được quản lý có thể chạy, Codex yêu cầu bạn xem xét và tin cậy chính xác định nghĩa hook đó. Codex ghi nhận độ tin cậy dựa trên hàm băm hiện tại của hook, vì vậy hook mới hoặc đã thay đổi sẽ được đánh dấu để xem xét và bị bỏ qua cho đến khi được tin cậy.

Dùng /hooks trong CLI để kiểm tra nguồn hook, xem xét hook mới hoặc đã thay đổi, tin cậy hook hoặc vô hiệu hóa từng hook không được quản lý. Nếu có hook cần xem xét khi khởi động, Codex sẽ in cảnh báo yêu cầu bạn mở /hooks.

Hook được quản lý từ nguồn hệ thống, MDM, đám mây hoặc requirements.toml được đánh dấu là được quản lý, được chính sách tin cậy và không thể bị vô hiệu hóa trong trình duyệt hook của người dùng.

Đối với tác vụ tự động hóa dùng một lần đã kiểm tra nguồn hook bên ngoài Codex, hãy truyền --dangerously-bypass-hook-trust để chạy các hook đã bật mà không yêu cầu độ tin cậy hook được lưu cho lần gọi đó.

Cấu trúc cấu hình

Hook được tổ chức thành ba cấp:

  • Một sự kiện hook như PreToolUse, PostToolUse, PreCompact, SubagentStart hoặc Stop
  • Một nhóm bộ so khớp quyết định thời điểm sự kiện đó khớp
  • Một hoặc nhiều trình xử lý hook chạy khi nhóm bộ so khớp khớp
{
  "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
          }
        ]
      }
    ]
  }
}

Lưu ý:

  • description là siêu dữ liệu cấp cao nhất không bắt buộc cho tệp hooks.json. Thuộc tính này không thay đổi những hook nào sẽ chạy.
  • timeout được tính bằng giây.
  • Nếu bỏ qua timeout, Codex sử dụng 600 giây cho hầu hết các hook.
    • SessionEndInterrupt mặc định sử dụng 1 giây và hỗ trợ tối đa 3 giây.
  • statusMessage là không bắt buộc.
  • additionalContextLimit đặt lượng additionalContext mà một hook lệnh có thể gửi đến mô hình trước khi Codex lưu toàn bộ văn bản vào ổ đĩa và thay vào đó gửi một bản xem trước ngắn hơn. Xem Đầu ra hook lớn.
  • commandWindows là tùy chọn ghi đè lệnh không bắt buộc, chỉ dành cho Windows. Trong TOML, hãy dùng command_windows hoặc commandWindows.
  • Đặt async thành true để chạy hook lệnh trong nền.
  • Các trình xử lý commandmcp_tool được hỗ trợ. Các trình xử lý promptagent được phân tích cú pháp nhưng bị bỏ qua.
  • Các lệnh chạy với cwd của phiên làm thư mục làm việc.
  • Đối với các hook cục bộ của repo, nên phân giải đường dẫn từ thư mục gốc git thay vì sử dụng đường dẫn tương đối như .codex/hooks/.... Codex có thể được khởi động từ một thư mục con, và đường dẫn dựa trên thư mục gốc git giúp vị trí của hook luôn ổn định.

TOML nội tuyến tương đương trong 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"

Hook công cụ MCP

Hook công cụ MCP cho phép một sự kiện vòng đời gọi công cụ trên một máy chủ MCP đã kết nối. Hook gửi trực tiếp các đối số có cấu trúc đến công cụ và dùng cùng quy trình xem xét độ tin cậy cũng như hợp đồng đầu ra như hook lệnh.

Cấu hình hook công cụ MCP

Hook này yêu cầu máy chủ MCP scanner quét từng bản vá sau khi Codex ghi hoặc chỉnh sửa tệp:

{
  "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"
          }
        ]
      }
    ]
  }
}
Trường Ý nghĩa
type Phải là mcp_tool.
server Tên bắt buộc của một máy chủ MCP đã kết nối.
tool Tên bắt buộc của một công cụ do máy chủ đó cung cấp.
input Đối tượng JSON không bắt buộc chứa mẫu đối số. Mặc định là {}.
timeout Thời gian chờ thực thi đang hoạt động không bắt buộc, tính bằng giây. Mặc định là 600.
statusMessage Thông báo không bắt buộc hiển thị trong khi hook chạy.

Mở rộng đối số từ sự kiện hook

Dùng ${field.nested} để đọc một trường phân cấp bằng dấu chấm từ sự kiện hook. Placeholder chiếm toàn bộ một giá trị sẽ giữ nguyên kiểu JSON. Placeholder nằm trong một chuỗi lớn hơn sẽ được kết xuất dưới dạng văn bản. Codex mở rộng đệ quy các đối tượng và mảng.

Với sự kiện chứa {"tool_input":{"file_path":"src/main.rs","count":3}}, mẫu đối số này:

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

sẽ trở thành:

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

Thực thi và vòng đời

  • Hook dùng kết nối MCP hiện có. Hook không khởi động hoặc kết nối lại máy chủ.
  • Hook có thể chặn một thao tác khi công cụ trả về quyết định chặn. Lỗi, máy chủ bị thiếu và công cụ không khả dụng không chặn thao tác.
  • Hook công cụ MCP chạy đồng bộ. Hook không yêu cầu phê duyệt công cụ hoặc kích hoạt hook khác.
  • Thời gian chờ ngắn hơn giữa hook và máy chủ sẽ được áp dụng. Thời gian chờ phản hồi gợi ý từ MCP không được tính vào thời gian chờ.
  • Hook SessionStart có thể chạy trước khi máy chủ MCP sẵn sàng. Nếu điều đó xảy ra, chúng không chặn phiên.
  • SessionEnd không hỗ trợ hook công cụ MCP.

Tắt hook

Hook được bật theo mặc định. Để tắt chúng trong config.toml, hãy đặt:

[features]
hooks = false

Dùng hooks làm khóa tính năng chuẩn. codex_hooks vẫn hoạt động như một bí danh không còn được khuyến nghị. Quản trị viên có thể buộc tắt hook theo cùng cách trong requirements.toml bằng [features].hooks = false.

Hook được quản lý từ requirements.toml

Các yêu cầu do doanh nghiệp quản lý cũng có thể định nghĩa hook nội tuyến bên dưới [hooks]. Điều này hữu ích khi quản trị viên muốn thực thi cấu hình hook trong khi phân phối các tập lệnh thực tế qua MDM hoặc một hệ thống quản lý thiết bị khác. Để thực thi hook được quản lý ngay cả với người dùng đã tắt hook cục bộ, hãy ghim [features].hooks = true trong requirements.toml cùng với [hooks]. Để bỏ qua hook người dùng, dự án, phiên và plugin nhưng vẫn cho phép hook do quản trị viên quản lý, hãy đặt 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"

Lưu ý về hook được quản lý:

  • managed_dir được dùng trên macOS và Linux.
  • windows_managed_dir được dùng trên Windows.
  • Codex không phân phối các tập lệnh trong managed_dir; công cụ doanh nghiệp của bạn phải cài đặt và cập nhật chúng riêng.
  • Lệnh hook được quản lý nên dùng đường dẫn tập lệnh tuyệt đối bên dưới thư mục được quản lý đã cấu hình.
  • allow_managed_hooks_only = true bỏ qua hook từ nguồn người dùng, dự án, phiên và plugin, nhưng vẫn tải hook được quản lý từ requirements.toml và các lớp cấu hình được quản lý khác.

Hook đi kèm plugin

Khi một plugin được bật, Codex có thể tải hook vòng đời từ plugin đó cùng với hook người dùng, dự án và được quản lý.

Theo mặc định, Codex tìm hooks/hooks.json bên trong thư mục gốc plugin. Manifest của plugin có thể ghi đè giá trị mặc định đó bằng một mục hooks trong .codex-plugin/plugin.json. Mục manifest có thể là một đường dẫn có tiền tố ./, mảng đường dẫn có tiền tố ./, đối tượng hook nội tuyến hoặc mảng đối tượng hook nội tuyến.

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

Đường dẫn hook trong manifest được phân giải tương đối với thư mục gốc plugin và phải nằm trong thư mục gốc đó. Nếu manifest định nghĩa hooks, Codex dùng các mục manifest đó thay cho hooks/hooks.json mặc định.

Lệnh hook plugin nhận các biến môi trường sau:

  • PLUGIN_ROOT là phần mở rộng riêng của Codex trỏ đến thư mục gốc của plugin đã cài đặt.
  • PLUGIN_DATA là phần mở rộng riêng của Codex trỏ đến thư mục dữ liệu có thể ghi của plugin.
  • Codex cũng đặt CLAUDE_PLUGIN_ROOTCLAUDE_PLUGIN_DATA để tương thích với các hook plugin hiện có.

Hook plugin dùng cùng lược đồ sự kiện như các hook khác. Việc cài đặt hoặc bật plugin không tự động đặt hook của plugin là đáng tin cậy; Codex bỏ qua hook đi kèm plugin cho đến khi bạn xem xét và tin cậy định nghĩa hook hiện tại.

Mẫu bộ so khớp

Trường matcher là một chuỗi biểu thức chính quy lọc thời điểm hook kích hoạt. Dùng "*", "" hoặc bỏ qua hoàn toàn matcher để khớp mọi lần xuất hiện của một sự kiện được hỗ trợ.

Chỉ một số sự kiện Codex hiện tại tuân theo matcher:

Sự kiện Nội dung được matcher lọc Ghi chú
PermissionRequest tên công cụ Phạm vi hỗ trợ bao gồm Bash, apply_patch* và tên công cụ MCP
PostToolUse tên công cụ Xem Phạm vi công cụ
PostCompact tác nhân kích hoạt nén ngữ cảnh Các giá trị là manual hoặc auto
PreCompact tác nhân kích hoạt nén ngữ cảnh Các giá trị là manual hoặc auto
PreToolUse tên công cụ Xem Phạm vi công cụ
SessionEnd lý do kết thúc Hiện chỉ có other
SessionStart nguồn bắt đầu Các giá trị là startup, resume, clearcompact
SubagentStart loại tác nhân phụ Các giá trị phụ thuộc vào tác nhân phụ được khởi động
SubagentStop loại tác nhân phụ Các giá trị phụ thuộc vào tác nhân phụ dừng hoạt động
UserPromptSubmit không được hỗ trợ Mọi matcher đã cấu hình đều bị bỏ qua cho sự kiện này
Stop không được hỗ trợ Mọi matcher đã cấu hình đều bị bỏ qua cho sự kiện này
Interrupt không được hỗ trợ Mọi matcher đã cấu hình đều bị bỏ qua cho sự kiện này

*Đối với apply_patch, các giá trị matcher cũng có thể dùng Edit hoặc Write.

Ví dụ:

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

Phạm vi hỗ trợ công cụ

PreToolUsePostToolUse có thể quan sát nhiều thứ hơn lệnh shell và lệnh gọi MCP. Hầu hết công cụ hàm cục bộ dùng cùng đường dẫn hook, vì vậy bạn có thể khớp tên công cụ, kiểm tra các đối số JSON và, đối với PreToolUse, chặn hoặc viết lại lệnh gọi.

Đường dẫn công cụ PreToolUse PostToolUse Lưu ý
Lệnh shell Khớp dưới dạng Bash.
Thực thi hợp nhất (exec_command) Khớp dưới dạng Bash. Một lần thăm dò write_stdin sau đó có thể chuyển PostToolUse của lệnh gốc khi lệnh đó hoàn tất.
apply_patch Khớp dưới dạng apply_patch, Edit hoặc Write.
Công cụ MCP Khớp tên công cụ MCP, chẳng hạn mcp__filesystem__read_file.
Công cụ hàm cục bộ khác Khớp tên công cụ hàm, chẳng hạn update_plan. spawn_agent cũng khớp Agent.
Công cụ được lưu trữ, như WebSearch Không Không Các công cụ này không dùng đường dẫn hook công cụ hàm cục bộ.

write_stdin là phương tiện truyền tải cho một phiên thực thi hợp nhất hiện có. Nó không chạy lại PreToolUse khi gửi đầu vào hoặc thăm dò một lệnh đã vượt qua PreToolUse.

Một số đường dẫn công cụ chuyên biệt có thể chọn không dùng đường dẫn hook mặc định. Hãy coi hook công cụ là một biện pháp bảo vệ hữu ích, không phải ranh giới thực thi toàn diện.

Các trường đầu vào chung

Mỗi hook lệnh nhận một đối tượng JSON trên stdin.

Đây là các trường dùng chung mà bạn thường sử dụng:

Trường Kiểu Ý nghĩa
session_id string ID phiên Codex hiện tại. Hook tác nhân phụ dùng ID phiên cha.
transcript_path string | null Đường dẫn đến tệp bản chép lời của phiên, nếu có
cwd string Thư mục làm việc của phiên
hook_event_name string Tên sự kiện hook hiện tại
model string Phần mở rộng riêng của Codex. Slug mô hình đang hoạt động

Hook trong phạm vi lượt liệt kê turn_id dưới dạng phần mở rộng riêng của Codex trong các bảng dành riêng cho sự kiện.

SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop, StopInterrupt cũng bao gồm permission_mode, mô tả chế độ quyền hiện tại là default, acceptEdits, plan, dontAsk hoặc bypassPermissions.

transcript_path trỏ đến bản chép lời cuộc trò chuyện để thuận tiện, nhưng định dạng bản chép lời không phải là giao diện ổn định cho hook và có thể thay đổi theo thời gian.

Nếu bạn cần định dạng truyền tải đầy đủ, hãy xem Lược đồ.

Các trường đầu ra chung

SessionStart, PreCompact, PostCompact, UserPromptSubmit, SubagentStopStop hỗ trợ các trường JSON dùng chung sau. SubagentStart chấp nhận cùng cấu trúc cho systemMessage và ngữ cảnh riêng của hook, nhưng continue: false không dừng tác nhân phụ:

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
Trường Tác dụng
continue Nếu là false, đánh dấu lần chạy hook đó là đã dừng
stopReason Được ghi lại làm lý do dừng
systemMessage Được hiển thị dưới dạng cảnh báo trong UI hoặc luồng sự kiện
suppressOutput Hiện được phân tích cú pháp nhưng chưa triển khai

Thoát với 0 mà không có đầu ra được coi là thành công và Codex tiếp tục.

PreToolUsePermissionRequest hỗ trợ systemMessage, nhưng continue, stopReasonsuppressOutput hiện không được hỗ trợ cho các sự kiện đó. Nếu một hook PreToolUse trả về một trong các trường không được hỗ trợ này, Codex đánh dấu lần chạy hook đó là thất bại, báo lỗi và tiếp tục lệnh gọi công cụ.

PostToolUse hỗ trợ systemMessage, continue: falsestopReason. suppressOutput được phân tích cú pháp nhưng hiện không được hỗ trợ cho sự kiện đó.

Đầu ra hook lớn

Theo mặc định, Codex giới hạn mỗi thông báo đầu ra hook mà mô hình nhìn thấy ở khoảng 2.500 token. Nếu hook trả về nhiều hơn, Codex lưu toàn bộ văn bản bên dưới <temp_dir>/hook_outputs/<session_id>/<uuid>.txt và cung cấp cho mô hình bản xem trước gồm phần đầu và cuối cùng với đường dẫn tệp đã lưu. Hành vi này được gọi là spilling: Codex lưu đầu ra quá lớn vào đĩa và thay thế bằng một bản xem trước ngắn hơn mà mô hình có thể thấy. Nếu không thể ghi tệp, mô hình vẫn nhận được bản xem trước đã cắt ngắn.

Với mọi hook lệnh trả về additionalContext, hãy đặt additionalContextLimit trên trình xử lý để tùy chỉnh ngưỡng token xấp xỉ:

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

Bỏ qua additionalContextLimit để dùng ngưỡng mặc định 2500 token. Dùng một số nguyên dương để chọn ngưỡng khác hoặc 0 để chuyển toàn bộ ngữ cảnh bổ sung của trình xử lý trực tiếp đến mô hình. Codex đánh giá độc lập từng trình xử lý khớp. Với các sự kiện không thể tạo ngữ cảnh bổ sung, Codex bỏ qua additionalContextLimit và báo cảnh báo cấu hình.

Cài đặt này chỉ áp dụng cho additionalContext. Phản hồi công cụ và prompt tiếp tục vẫn dùng giới hạn mặc định.

Vì đầu ra quá lớn có thể được ghi vào đĩa, hãy tránh trả về bí mật hoặc dữ liệu nhạy cảm khác trong đầu ra hook.

Chạy hook trong nền

Theo mặc định, Codex đợi hook lệnh hoàn tất trước khi tiếp tục thao tác đã kích hoạt hook đó. Đặt async thành true để chạy hook lệnh trong nền trong khi Codex tiếp tục.

Cấu hình hook nền

Thêm "async": true vào trình xử lý lệnh trong hooks.json:

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

Với hook nội tuyến trong config.toml, hãy đặt async = true:

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

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

Các hook chạy nền sử dụng cùng dữ liệu đầu vào, matcher, quy trình xét duyệt độ tin cậy, thời gian chờ và cơ chế xử lý đầu ra lớn như các hook lệnh đồng bộ. Tương tự các hook lệnh khác, timeout được tính bằng giây và mặc định là 600. Các hook Interrupt sử dụng giá trị mặc định là một giây và tối đa là ba giây, kể cả khi chạy trong nền.

Cách hook nền chạy

Khi một hook nền hoàn tất, Codex chuyển đầu ra thông tin được hỗ trợ ở thời điểm an toàn tiếp theo trong cuộc trò chuyện:

  • Nếu một lượt đang hoạt động, Codex đợi yêu cầu mô hình và các lệnh gọi công cụ hiện tại hoàn tất, sau đó cung cấp đầu ra cho yêu cầu mô hình tiếp theo trong lượt đó.
  • Nếu không có lượt nào đang hoạt động, Codex đợi đến lượt người dùng tiếp theo. Việc hoàn tất một hook nền không bắt đầu lượt mới.

Dùng cùng đầu ra JSON dành riêng cho sự kiện như hook đồng bộ. Codex thêm additionalContext vào ngữ cảnh của mô hình và hiển thị systemMessage dưới dạng cảnh báo.

Giới hạn

  • Codex chạy đồng thời tối đa tám hook nền cho mỗi phiên. Hook bổ sung sẽ đợi cho đến khi một hook đang chạy hoàn tất.
  • Mỗi lần gọi khớp chạy độc lập và hook nền có thể hoàn tất theo thứ tự khác với lúc bắt đầu.
  • Khi phiên kết thúc, Codex hủy các hook nền chưa hoàn tất và loại bỏ đầu ra chưa được chuyển.
  • Hook SessionEnd luôn chạy đồng bộ.

Hook

SessionStart

matcher được áp dụng cho source đối với sự kiện này.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
source string Cách phiên bắt đầu: startup, resume, clear hoặc compact

Văn bản thuần trên stdout được thêm làm ngữ cảnh nhà phát triển bổ sung.

JSON trên stdout hỗ trợ Các trường đầu ra chung và cấu trúc dành riêng cho hook này:

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

Văn bản additionalContext đó được thêm làm ngữ cảnh nhà phát triển bổ sung.

Sau khi Codex nén một phiên gốc, các hook SessionStart khớp với source: "compact" sẽ chạy trước yêu cầu mô hình tiếp theo. Điều này cũng áp dụng khi quá trình nén tự động diễn ra giữa một lượt: Codex chuyển ngữ cảnh bổ sung của hook đến ngay lần tiếp tục, thay vì chờ một lượt người dùng sau đó. Nếu hook trả về continue: false, Codex kết thúc lượt mà không gửi yêu cầu mô hình khác.

SessionEnd

SessionEnd cho phép bạn chạy lệnh khi một phiên kết thúc, chẳng hạn lưu ghi chú cuối cùng hoặc dọn dẹp tệp. Hook chạy cho luồng chính khi bạn lưu trữ hoặc xóa một cuộc trò chuyện vẫn đang mở, khi Codex đóng bình thường hoặc sau khi một cuộc trò chuyện không hoạt động và không được mở trong bất kỳ ứng dụng kết nối nào trong 30 phút. Hook không chạy cho tác nhân phụ.

Việc chuyển khỏi cuộc trò chuyện hoặc gọi thread/unsubscribe không kết thúc phiên ngay lập tức, vì vậy thao tác đó không chạy SessionEnd ngay. Hook của bạn vẫn có thể đọc bản chép lời phiên trong khi chạy.

matcher lọc reason cho sự kiện này. Hiện tại, reason luôn là other. Bạn có thể bỏ qua matcher hoặc dùng other để chạy trên mọi sự kiện SessionEnd.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
reason string Lý do phiên kết thúc: other

Ví dụ, một lệnh SessionEnd nhận:

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

Hook SessionEnd luôn chạy đồng bộ, ngay cả khi asynctrue. Chúng mang tính tư vấn nên đầu ra sẽ không định hướng Codex hoặc giữ luồng mở. Nếu một lệnh hết thời gian chờ hoặc thoát với lỗi, Codex báo đó là lỗi hook.

SubagentStart

matcher được áp dụng cho agent_type đối với sự kiện này.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
agent_id string Mã định danh của tác nhân phụ
agent_type string Loại hoặc hồ sơ tác nhân phụ
permission_mode string Chế độ quyền hiện tại

Văn bản thuần trên stdout được thêm làm ngữ cảnh nhà phát triển bổ sung cho tác nhân phụ.

JSON trên stdout hỗ trợ systemMessage và cấu trúc dành riêng cho hook này:

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

Văn bản additionalContext đó được thêm làm ngữ cảnh nhà phát triển bổ sung cho tác nhân phụ. continue: false được phân tích cú pháp để đảm bảo tính tương thích, nhưng không ngăn tác nhân phụ bắt đầu.

PreToolUse

PreToolUse có thể chặn giữa Bash, thao tác chỉnh sửa tệp thực hiện qua apply_patch, lệnh gọi công cụ MCP và các công cụ hàm cục bộ khác. Xem Phạm vi hỗ trợ công cụ để biết các đường dẫn được hỗ trợ và ngoại lệ.

matcher được áp dụng cho tool_name và các bí danh của bộ so khớp. Đối với thao tác chỉnh sửa tệp qua apply_patch, giá trị matcher có thể dùng apply_patch, Edit hoặc Write; đầu vào hook vẫn báo tool_name: "apply_patch".

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
tool_name string Tên công cụ hook chuẩn, chẳng hạn Bash, apply_patch hoặc tên MCP như mcp__fs__read
tool_use_id string ID lệnh gọi công cụ cho lần gọi này
tool_input JSON value Đầu vào dành riêng cho công cụ. Bashapply_patch dùng tool_input.command. MCP và các công cụ hàm cục bộ khác gửi đối số của chúng.

Văn bản thuần trên stdout bị bỏ qua.

JSON trên stdout có thể dùng systemMessage. Để từ chối một lệnh gọi công cụ được hỗ trợ, hãy trả về cấu trúc dành riêng cho hook này:

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

Codex cũng chấp nhận cấu trúc khối cũ hơn này:

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

Bạn cũng có thể dùng mã thoát 2 và ghi lý do chặn vào stderr.

Để thêm ngữ cảnh mà mô hình nhìn thấy mà không chặn, hãy trả về hookSpecificOutput.additionalContext:

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

Để viết lại một lệnh gọi công cụ được hỗ trợ mà không chặn, hãy trả về permissionDecision: "allow" cùng với updatedInput:

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

Đối với lệnh Bash và apply_patch, updatedInput phải bao gồm trường chuỗi command. Đối với MCP và các công cụ hàm cục bộ khác, updatedInput là đối tượng đối số thay thế. Chỉ trả về updatedInput cùng với permissionDecision: "allow"; các cấu trúc updatedInput khác sẽ bị báo là lỗi.

permissionDecision: "ask", decision: "approve" cũ, continue: false, stopReasonsuppressOutput được phân tích cú pháp nhưng chưa được hỗ trợ. Codex đánh dấu lần chạy hook là thất bại, báo lỗi và tiếp tục lệnh gọi công cụ.

PermissionRequest

PermissionRequest chạy khi Codex sắp yêu cầu phê duyệt, chẳng hạn như nâng cấp quyền shell hoặc phê duyệt mạng được quản lý. Hook có thể cho phép yêu cầu, từ chối yêu cầu hoặc không đưa ra quyết định để lời nhắc phê duyệt thông thường tiếp tục. Hook không chạy cho các lệnh không cần phê duyệt.

matcher được áp dụng cho tool_name và các bí danh của bộ so khớp. Các giá trị chuẩn hiện tại bao gồm Bash, apply_patch và tên công cụ MCP như mcp__server__tool; apply_patch cũng khớp EditWrite.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
tool_name string Tên công cụ hook chuẩn, chẳng hạn Bash, apply_patch hoặc tên MCP như mcp__fs__read
tool_input JSON value Đầu vào dành riêng cho công cụ. Bashapply_patch dùng tool_input.command, còn công cụ MCP gửi tất cả đối số.
tool_input.description string | null Lý do phê duyệt mà con người có thể đọc, khi Codex có lý do đó

Văn bản thuần trên stdout bị bỏ qua.

Một số đầu vào công cụ có thể bao gồm mô tả mà con người có thể đọc, nhưng đừng phụ thuộc vào trường tool_input.description cho mọi công cụ.

Để phê duyệt yêu cầu, hãy trả về:

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

Để từ chối yêu cầu, hãy trả về:

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

Nếu nhiều hook khớp trả về quyết định, bất kỳ deny nào cũng được ưu tiên. Nếu không, một allow cho phép yêu cầu tiếp tục mà không hiển thị lời nhắc phê duyệt. Nếu không có hook khớp nào đưa ra quyết định, Codex dùng luồng phê duyệt thông thường.

Không trả về updatedInput, updatedPermissions hoặc interrupt cho PermissionRequest; các trường này được dành cho hành vi trong tương lai và hiện sẽ từ chối để bảo đảm an toàn.

PostToolUse

PostToolUse chạy sau khi công cụ được hỗ trợ tạo đầu ra, bao gồm Bash, apply_patch, lệnh gọi công cụ MCP và các công cụ hàm cục bộ khác. Đối với Bash, hook cũng chạy sau các lệnh thoát với trạng thái khác không. Hook không thể hoàn tác tác dụng phụ của một công cụ đã chạy. Xem Phạm vi hỗ trợ công cụ để biết các đường dẫn được hỗ trợ và ngoại lệ.

matcher được áp dụng cho tool_name và các bí danh của bộ so khớp. Đối với thao tác chỉnh sửa tệp qua apply_patch, giá trị matcher có thể dùng apply_patch, Edit hoặc Write; đầu vào hook vẫn báo tool_name: "apply_patch".

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
tool_name string Tên công cụ hook chuẩn, chẳng hạn Bash, apply_patch hoặc tên MCP như mcp__fs__read
tool_use_id string ID lệnh gọi công cụ cho lần gọi này
tool_input JSON value Đầu vào dành riêng cho công cụ. Bashapply_patch dùng tool_input.command. MCP và các công cụ hàm cục bộ khác gửi đối số của chúng.
tool_response JSON value Đầu ra dành riêng cho công cụ. Công cụ MCP gửi kết quả lệnh gọi MCP. Các công cụ hàm cục bộ khác thường gửi đầu ra dành cho mô hình.

Văn bản thuần trên stdout bị bỏ qua.

JSON trên stdout có thể dùng systemMessage và cấu trúc dành riêng cho hook này:

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

Văn bản additionalContext đó được thêm làm ngữ cảnh nhà phát triển bổ sung.

Đối với sự kiện này, decision: "block" không hoàn tác lệnh Bash đã hoàn tất. Thay vào đó, Codex ghi lại phản hồi, thay thế kết quả công cụ bằng phản hồi đó và tiếp tục mô hình từ thông báo do hook cung cấp.

Bạn cũng có thể dùng mã thoát 2 và ghi lý do phản hồi vào stderr.

Để dừng quá trình xử lý bình thường đối với kết quả công cụ gốc sau khi lệnh đã chạy, hãy trả về continue: false. Codex sẽ thay thế kết quả công cụ bằng phản hồi hoặc văn bản dừng của bạn rồi tiếp tục từ đó.

updatedMCPToolOutputsuppressOutput được phân tích cú pháp nhưng chưa được hỗ trợ. Codex đánh dấu lần chạy hook là thất bại, báo lỗi và tiếp tục xử lý kết quả công cụ theo cách thông thường.

Lệnh gọi công cụ từ chế độ mã

Khi mô hình dùng chế độ mã để gọi công cụ từ JavaScript, quyết định của hook được áp dụng cho lệnh gọi lồng nhau đó. PreToolUse có thể dừng công cụ trước khi chạy hoặc viết lại đầu vào của công cụ. Một PostToolUse chặn không thể hoàn tác tác dụng phụ của công cụ, nhưng có thể ngăn kết quả gốc đến được tập lệnh đang chạy.

Kết quả hook Nội dung chế độ mã nhận được
PreToolUse chặn Promise của công cụ bị từ chối trước khi công cụ chạy.
PreToolUse trả về updatedInput Công cụ chạy với đầu vào đã viết lại và promise được phân giải bằng kết quả đó.
PostToolUse trả về decision: "block" hoặc thoát với mã 2 Công cụ chạy, sau đó promise bị từ chối với lý do của hook.
PostToolUse trả về continue: false Codex dùng phản hồi của hook làm kết quả mà mô hình nhìn thấy, nhưng không từ chối promise công cụ lồng nhau.

PreCompact

PreCompact chạy trước khi Codex nén cuộc trò chuyện. matcher được áp dụng cho trigger, có các giá trị là manualauto.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
trigger string Yếu tố kích hoạt nén: manual hoặc auto

Văn bản thuần trên stdout bị bỏ qua.

JSON trên stdout hỗ trợ Các trường đầu ra chung. Nếu một hook PreCompact khớp trả về continue: false, Codex dừng trước khi nén.

PostCompact

PostCompact chạy sau khi Codex nén cuộc trò chuyện. matcher được áp dụng cho trigger, có các giá trị là manualauto.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
trigger string Yếu tố kích hoạt nén: manual hoặc auto

Văn bản thuần trên stdout bị bỏ qua.

JSON trên stdout hỗ trợ Các trường đầu ra chung. Nếu một hook PostCompact khớp trả về continue: false, Codex dừng sau khi nén.

UserPromptSubmit

matcher hiện không được dùng cho sự kiện này.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
prompt string Prompt người dùng sắp được gửi

Văn bản thuần trên stdout được thêm làm ngữ cảnh nhà phát triển bổ sung.

JSON trên stdout hỗ trợ Các trường đầu ra chung và cấu trúc dành riêng cho hook này:

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

Văn bản additionalContext đó được thêm làm ngữ cảnh nhà phát triển bổ sung.

Để chặn prompt, hãy trả về:

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

Bạn cũng có thể dùng mã thoát 2 và ghi lý do chặn vào stderr.

SubagentStop

matcher được áp dụng cho agent_type đối với sự kiện này.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
agent_id string Mã định danh của tác nhân phụ
agent_type string Loại hoặc hồ sơ tác nhân phụ
agent_transcript_path string | null Đường dẫn đến tệp bản chép lời của tác nhân phụ, nếu có
stop_hook_active boolean Tác nhân phụ này đã được tiếp tục hay chưa
last_assistant_message string | null Thông báo mới nhất của trợ lý tác nhân phụ, nếu có

SubagentStop yêu cầu JSON trên stdout khi thoát với 0. Đầu ra văn bản thuần không hợp lệ cho sự kiện này.

JSON trên stdout hỗ trợ Các trường đầu ra chung. Để yêu cầu Codex tiếp tục luồng tác nhân phụ, hãy trả về:

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

Bạn cũng có thể dùng mã thoát 2 và ghi lý do tiếp tục vào stderr.

Nếu bất kỳ hook SubagentStop khớp nào trả về continue: false, quyết định đó được ưu tiên hơn quyết định tiếp tục từ các hook SubagentStop khớp khác.

Stop

matcher hiện không được dùng cho sự kiện này.

Các trường bổ sung ngoài Các trường đầu vào chung:

Trường Kiểu Ý nghĩa
turn_id string Phần mở rộng riêng của Codex. ID lượt Codex đang hoạt động
stop_hook_active boolean Lượt này đã được Stop tiếp tục hay chưa
last_assistant_message string | null Văn bản thông báo mới nhất của trợ lý, nếu có

Stop yêu cầu JSON trên stdout khi thoát với 0. Đầu ra văn bản thuần không hợp lệ cho sự kiện này.

JSON trên stdout hỗ trợ Các trường đầu ra chung. Để Codex tiếp tục chạy, hãy trả về:

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

Bạn cũng có thể dùng mã thoát 2 và ghi lý do tiếp tục vào stderr.

Đối với sự kiện này, decision: "block" không từ chối lượt. Thay vào đó, trường này yêu cầu Codex tiếp tục và tự động tạo một prompt tiếp tục mới hoạt động như một prompt người dùng mới, dùng reason của bạn làm văn bản prompt đó.

Nếu bất kỳ hook Stop khớp nào trả về continue: false, quyết định đó được ưu tiên hơn quyết định tiếp tục từ các hook Stop khớp khác.

Ngắt

Interrupt chạy khi bạn ngắt một lượt đang hoạt động trên luồng chính. Hãy dùng hook này để ghi lại lần ngắt hoặc dọn dẹp công việc do một hook khởi tạo. Hook này không chạy cho các luồng không hoạt động hoặc tác nhân phụ, và mọi matcher đã cấu hình đều bị bỏ qua.

Ngoài Các trường đầu vào chung, sự kiện còn bao gồm turn_id, id của lượt bị ngắt và permission_mode.

Các hook lệnh mặc định có thời gian chờ một giây. Thời gian chờ đã cấu hình được giới hạn từ một đến ba giây. Đầu ra của hook không thể ngăn việc ngắt hoặc khởi động lại lượt. Thoát với 0 mà không có đầu ra, hoặc trả về JSON có systemMessage không bắt buộc để hiển thị cảnh báo. Đầu ra văn bản thuần không hợp lệ cho sự kiện này.

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

Lược đồ

Nếu bạn cần định dạng truyền tải chính xác hiện tại, hãy xem các lược đồ được tạo trong kho lưu trữ GitHub của Codex.

Bí danh văn bản thuần

  • string | null