Skip to content
FunCoding

Search

Search docs, agents, posts, Skills and MCP servers

Python SDK 参考:Hook 类型

Agent SDK Python 的 HookEvent、HookCallback、HookContext、HookMatcher、各事件的 HookInput 类型(PreToolUse、PostToolUse、Stop、PermissionRequest 等)、HookJSONOutput 同步与异步输出,以及完整使用示例。

本页是 Python Agent SDK 参考的第四部分:ClaudeAgentOptions.hooks 用到的类型。用 hooks 的指南和常见模式见「Agent SDK:Hooks」。

HookEvent

支持的 hook 事件类型。

HookEvent = Literal[
    "PreToolUse",  # 工具执行之前调用
    "PostToolUse",  # 工具执行之后调用
    "PostToolUseFailure",  # 工具执行失败时调用
    "UserPromptSubmit",  # 用户提交提示时调用
    "Stop",  # 停止执行时调用
    "SubagentStop",  # 子智能体停止时调用
    "PreCompact",  # 消息压缩之前调用
    "Notification",  # 通知事件时调用
    "SubagentStart",  # 子智能体启动时调用
    "PermissionRequest",  # 需要权限决定时调用
]

TypeScript SDK 支持 Python 里尚不可用的额外 hook 事件(每个 SDK 的支持情况见 hook 可用性表)。

HookCallback、HookContext、HookMatcher

hook 回调函数的类型定义:

HookCallback = Callable[[HookInput, str | None, HookContext], Awaitable[HookJSONOutput]]

参数:input 是按 hook_event_name 区分的强类型 hook 输入(见 HookInput);tool_use_id 是可选的工具使用标识(用于与工具相关的 hooks);context 是带附加信息的 hook 上下文。返回 HookJSONOutput。

class HookContext(TypedDict):
    signal: Any | None  # 未来:中止信号支持

把 hooks 匹配到特定事件或工具的配置:

@dataclass
class HookMatcher:
    matcher: str | None = (
        None  # 要匹配的工具名或模式(例如 "Bash"、"Write|Edit")
    )
    hooks: list[HookCallback] = field(
        default_factory=list
    )  # 要执行的回调列表
    timeout: float | None = (
        None  # 超时秒数。省略时适用每事件的默认值:
        # 多数事件 600,UserPromptSubmit 30
    )

HookInput 与 BaseHookInput

所有 hook 输入类型的联合类型,实际类型取决于 hook_event_name 字段:

HookInput = (
    PreToolUseHookInput
    | PostToolUseHookInput
    | PostToolUseFailureHookInput
    | UserPromptSubmitHookInput
    | StopHookInput
    | SubagentStopHookInput
    | PreCompactHookInput
    | NotificationHookInput
    | SubagentStartHookInput
    | PermissionRequestHookInput
)

所有 hook 输入类型里都有的基础字段:

class BaseHookInput(TypedDict):
    session_id: str
    transcript_path: str
    cwd: str
    permission_mode: NotRequired[str]
字段类型说明
session_idstr当前会话标识
transcript_pathstr会话记录文件的路径
cwdstr当前工作目录
permission_modestr(可选)当前权限模式

各事件的输入类型

PreToolUseHookInput:PreToolUse 事件的输入数据。

class PreToolUseHookInput(BaseHookInput):
    hook_event_name: Literal["PreToolUse"]
    tool_name: str
    tool_input: dict[str, Any]
    tool_use_id: str
    agent_id: NotRequired[str]
    agent_type: NotRequired[str]

hook_event_name 总是 "PreToolUse";tool_name 是即将执行的工具名;tool_input 是工具的输入参数;tool_use_id 是这次工具使用的唯一标识;agent_id 和 agent_type(可选)是 hook 在子智能体内触发时存在的子智能体标识和类型。

PostToolUseHookInput:PostToolUse 事件的输入数据。

class PostToolUseHookInput(BaseHookInput):
    hook_event_name: Literal["PostToolUse"]
    tool_name: str
    tool_input: dict[str, Any]
    tool_response: Any
    tool_use_id: str
    agent_id: NotRequired[str]
    agent_type: NotRequired[str]

tool_name 是已执行的工具名,tool_input 是使用的输入参数,tool_response 是工具执行的响应,其余字段同上。

PostToolUseFailureHookInput:PostToolUseFailure 事件的输入数据,在工具执行失败时调用。

class PostToolUseFailureHookInput(BaseHookInput):
    hook_event_name: Literal["PostToolUseFailure"]
    tool_name: str
    tool_input: dict[str, Any]
    tool_use_id: str
    error: str
    is_interrupt: NotRequired[bool]
    agent_id: NotRequired[str]
    agent_type: NotRequired[str]

error 是失败执行的错误消息;is_interrupt(可选)在失败以中止而不是工具报告的错误的形式到达 Claude Code 时为 True(用 interrupt() 取消运行中的工具不会触发这个 hook,工具结果里改为携带中断消息)。

UserPromptSubmitHookInput、StopHookInput、SubagentStopHookInput

class UserPromptSubmitHookInput(BaseHookInput):
    hook_event_name: Literal["UserPromptSubmit"]
    prompt: str


class StopHookInput(BaseHookInput):
    hook_event_name: Literal["Stop"]
    stop_hook_active: bool


class SubagentStopHookInput(BaseHookInput):
    hook_event_name: Literal["SubagentStop"]
    stop_hook_active: bool
    agent_id: str
    agent_transcript_path: str
    agent_type: str

prompt 是用户提交的提示;stop_hook_active 表示停止 hook 是否处于活动状态;agent_id 是子智能体的唯一标识,agent_transcript_path 是子智能体记录文件的路径,agent_type 是子智能体的类型。

PreCompactHookInput、NotificationHookInput、SubagentStartHookInput、PermissionRequestHookInput

class PreCompactHookInput(BaseHookInput):
    hook_event_name: Literal["PreCompact"]
    trigger: Literal["manual", "auto"]
    custom_instructions: str | None


class NotificationHookInput(BaseHookInput):
    hook_event_name: Literal["Notification"]
    message: str
    title: NotRequired[str]
    notification_type: str


class SubagentStartHookInput(BaseHookInput):
    hook_event_name: Literal["SubagentStart"]
    agent_id: str
    agent_type: str


class PermissionRequestHookInput(BaseHookInput):
    hook_event_name: Literal["PermissionRequest"]
    tool_name: str
    tool_input: dict[str, Any]
    permission_suggestions: NotRequired[list[Any]]
    agent_id: NotRequired[str]
    agent_type: NotRequired[str]

trigger 是什么触发了压缩,custom_instructions 是压缩的自定义说明;message、title(可选)和 notification_type 是通知的内容、标题和类型;PermissionRequestHookInput 让 hooks 能以编程方式处理权限决定,permission_suggestions(可选)是来自 CLI 的建议权限更新。

HookJSONOutput

hook 回调返回值的联合类型。

HookJSONOutput = AsyncHookJSONOutput | SyncHookJSONOutput

SyncHookJSONOutput

带控制和决定字段的同步 hook 输出。

class SyncHookJSONOutput(TypedDict):
    # 控制字段
    continue_: NotRequired[bool]  # 是否继续(默认:True)
    suppressOutput: NotRequired[bool]  # 从记录里隐藏 stdout
    stopReason: NotRequired[str]  # continue 为 False 时的消息

    # 决定字段
    decision: NotRequired[Literal["block"]]
    systemMessage: NotRequired[str]  # 给用户的警告消息
    reason: NotRequired[str]  # 给 Claude 的反馈

    # hook 专属输出
    hookSpecificOutput: NotRequired[HookSpecificOutput]

在 Python 代码里用 continue_(带下划线),发给 CLI 时它会被自动转换成 continue。

HookSpecificOutput

事件专属 TypedDict 输出类型的判别联合,hookEventName 字段决定哪些字段有效(每个 hook 事件可用字段的完整细节见「用 hooks 控制执行」)。

class PreToolUseHookSpecificOutput(TypedDict):
    hookEventName: Literal["PreToolUse"]
    permissionDecision: NotRequired[Literal["allow", "deny", "ask", "defer"]]
    permissionDecisionReason: NotRequired[str]
    updatedInput: NotRequired[dict[str, Any]]
    additionalContext: NotRequired[str]


class PostToolUseHookSpecificOutput(TypedDict):
    hookEventName: Literal["PostToolUse"]
    additionalContext: NotRequired[str]
    updatedToolOutput: NotRequired[Any]
    updatedMCPToolOutput: NotRequired[Any]  # 已弃用:改用对所有工具都有效的 updatedToolOutput


class PostToolUseFailureHookSpecificOutput(TypedDict):
    hookEventName: Literal["PostToolUseFailure"]
    additionalContext: NotRequired[str]


class UserPromptSubmitHookSpecificOutput(TypedDict):
    hookEventName: Literal["UserPromptSubmit"]
    additionalContext: NotRequired[str]


class NotificationHookSpecificOutput(TypedDict):
    hookEventName: Literal["Notification"]
    additionalContext: NotRequired[str]


class SubagentStartHookSpecificOutput(TypedDict):
    hookEventName: Literal["SubagentStart"]
    additionalContext: NotRequired[str]


class PermissionRequestHookSpecificOutput(TypedDict):
    hookEventName: Literal["PermissionRequest"]
    decision: dict[str, Any]


HookSpecificOutput = (
    PreToolUseHookSpecificOutput
    | PostToolUseHookSpecificOutput
    | PostToolUseFailureHookSpecificOutput
    | UserPromptSubmitHookSpecificOutput
    | NotificationHookSpecificOutput
    | SubagentStartHookSpecificOutput
    | PermissionRequestHookSpecificOutput
)

AsyncHookJSONOutput

推迟 hook 执行的异步 hook 输出。

class AsyncHookJSONOutput(TypedDict):
    async_: Literal[True]  # 设为 True 以推迟执行
    asyncTimeout: NotRequired[int]  # 超时毫秒数

在 Python 代码里用 async_(带下划线),发给 CLI 时它会被自动转换成 async。

hook 使用示例

这个例子注册两个 hooks:一个阻止 rm -rf / 这类危险的 Bash 命令,另一个记录所有工具使用以供审计。安全 hook 只在 Bash 命令上运行(通过 matcher),日志 hook 在所有工具上运行。

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, HookMatcher, HookContext
from typing import Any


async def validate_bash_command(
    input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
    """验证并可能阻止危险的 bash 命令。"""
    if input_data["tool_name"] == "Bash":
        command = input_data["tool_input"].get("command", "")
        if "rm -rf /" in command:
            return {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": "Dangerous command blocked",
                }
            }
    return {}


async def log_tool_use(
    input_data: dict[str, Any], tool_use_id: str | None, context: HookContext
) -> dict[str, Any]:
    """记录所有工具使用以供审计。"""
    print(f"Tool used: {input_data.get('tool_name')}")
    return {}


options = ClaudeAgentOptions(
    hooks={
        "PreToolUse": [
            HookMatcher(
                matcher="Bash", hooks=[validate_bash_command], timeout=120
            ),  # 验证用 2 分钟
            HookMatcher(
                hooks=[log_tool_use]
            ),  # 适用于所有工具(用每事件的默认超时)
        ],
        "PostToolUse": [HookMatcher(hooks=[log_tool_use])],
    }
)


async def main():
    async for message in query(prompt="Analyze this codebase", options=options):
        print(message)


asyncio.run(main())