Hook

本主题介绍如何使用 Hook 在代理生命周期的关键点运行自定义代码。Hook 允许您拦截工具调用、审计代理行为、强制执行策略、注入上下文以及控制执行流程。

Hook 的工作原理

Hook遵循一个四步流程:事件 被触发,SDK 检查是否有任何 匹配器 适用,然后调用您的 回调,您的回调返回一个 决策,该决策控制接下来发生什么。

  1. 代理触发一个事件(例如,它即将调用一个工具)。

  2. SDK 检查为该事件类型注册的每个 Hook 匹配器。

  3. 如果某个匹配器匹配(或者没有匹配器模式,意味着匹配所有情况),SDK 会调用关联的回调函数。

  4. 每个回调返回一个输出对象,该对象可以允许、阻止或修改操作。

Hook 事件

以下事件可用:

事件

触发时机

PreToolUse

在工具执行之前。可以阻止工具执行或修改其输入。

PostToolUse

在工具成功执行之后。可以注入额外的上下文。

PostToolUseFailure

在工具执行失败之后。

UserPromptSubmit

当用户发送提示时。可以注入额外的上下文。

Stop

当代理即将停止时。可以注入上下文或终止会话。

SubagentStart

当子代理启动时。

SubagentStop

当子代理即将停止时。

PreCompact

在对话被压缩(总结以适应上下文窗口)之前。

Notification

当代理发出通知时。

PermissionRequest

当发生工具权限检查时。

配置 Hook

在创建会话时,通过 hooks 选项传递 Hook。每个 Hook 事件映射到一个 匹配器 列表,每个匹配器包含一个回调函数列表。

import { createCortexCodeSession } from "cortex-code-agent-sdk";

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  hooks: {
    PreToolUse: [
      {
        matcher: "Bash",
        hooks: [
          async (input, toolUseId, context) => {
            console.log("Bash command:", input.tool_input);
            return {};
          },
        ],
      },
    ],
  },
});

匹配器

每个 Hook 匹配器包含三个字段:

字段

类型

描述

matcher

``string``(可选)

一个正则表达式模式,供支持匹配的事件使用。PreToolUsePostToolUsePermissionRequest 匹配工具名称。Notification 匹配通知类型。PreCompact 匹配触发器值("auto""manual")。如果省略,Hook 将针对该事件的所有值触发。

hooks

回调列表

当匹配器匹配时要运行的一个或多个回调函数。

timeout

``number``(可选)

此匹配器中所有回调的最长时间(秒)。默认值为 60。

matcher 字段接受任何有效的正则表达式模式。例如:

  • "Bash" – 精确匹配 Bash 工具

  • "Write|Edit" – 匹配 Write 或 Edit

  • ".*" – 匹配所有工具(效果等同于省略匹配器)

回调输入

每个回调接收三个实参:

实参

描述

input

一个包含事件特定数据的对象。所有事件都包含 session_idtranscript_pathcwdpermission_modehook_event_name。工具事件还包含 tool_nametool_inputtool_use_id

toolUseId / tool_use_id

工具使用 ID(用于工具相关事件)或 null

context

上下文对象。保留以供将来使用(例如,中止信号)。

输入字段因事件而异:

事件

其他输入字段

PreToolUse

tool_nametool_inputtool_use_id

PostToolUse

tool_nametool_inputtool_responsetool_use_id

PostToolUseFailure

tool_nametool_inputtool_use_iderror,可选的 is_interrupt

UserPromptSubmit

prompt

Stop

stop_hook_active

SubagentStart

agent_idagent_type

SubagentStop

stop_hook_activeagent_idagent_transcript_pathagent_type

PreCompact

trigger``("manual"`` 或 "auto")、custom_instructions

Notification

messagenotification_type,可选的 title

PermissionRequest

tool_nametool_input,可选的 permission_suggestions

工具生命周期 Hook 和 PermissionRequest 当由子代理触发时,还可以包含可选的 agent_idagent_type 字段。

回调输出

回调返回一个控制执行的输出对象。以下字段可用:

字段

类型

描述

continue / continue_

boolean

是否应继续处理。设置为 false 将停止当前 Hook 点或轮次的后续处理。默认:true

stopReason

string

continuefalse 时显示的消息。

decision

"block"

设置为 "block" 以阻止当前操作。

reason

string

向代理提供的关于该决策的反馈消息。

systemMessage

string

向用户显示的警告消息。

hookSpecificOutput

object

事件特定的控制(见下文)。

备注

Python SDK 使用 continue_``(带尾随下划线)而不是 ``continue 以避免 Python 关键字冲突。SDK 在与 CLI 通信时会自动将其转换为 continue

Hook 特定的输出

hookSpecificOutput 字段接受事件特定的控制:

PreToolUse

字段

描述

permissionDecision

"allow""deny""ask"。控制工具是否可以执行。

permissionDecisionReason

权限决策原因。

updatedInput

用于替代原始输入的修改后的工具输入。

PostToolUse

字段

描述

additionalContext

工具执行后注入到对话中的额外上下文。

UserPromptSubmit

字段

描述

additionalContext

注入到对话中的额外上下文。

PermissionRequest

字段

描述

decision.behavior

"allow""deny"。控制权限结果。

decision.message

拒绝权限请求时显示的消息。

示例

阻止危险工具

阻止代理运行特定的 Bash 命令:

import { createCortexCodeSession } from "cortex-code-agent-sdk";

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  hooks: {
    PreToolUse: [
      {
        matcher: "Bash",
        hooks: [
          async (input) => {
            const command = (input.tool_input as any)?.command ?? "";
            if (command.includes("rm -rf") || command.includes("DROP TABLE")) {
              return {
                decision: "block",
                reason: "Destructive commands are not allowed",
              };
            }
            return {};
          },
        ],
      },
    ],
  },
});

自动允许只读权限请求

允许仅读取数据的工具的权限请求:

hooks: {
  PermissionRequest: [
    {
      matcher: "Read|Glob|Grep",
      hooks: [
        async (input) => {
          return {
            hookSpecificOutput: {
              hookEventName: "PermissionRequest",
              decision: { behavior: "allow" },
            },
          };
        },
      ],
    },
  ],
}

修改工具输入

在所有 Bash 命令执行前为其添加超时:

hooks: {
  PreToolUse: [
    {
      matcher: "Bash",
      hooks: [
        async (input) => {
          const originalCommand = (input.tool_input as any)?.command ?? "";
          return {
            hookSpecificOutput: {
              hookEventName: "PreToolUse",
              updatedInput: { command: `timeout 30 ${originalCommand}` },
            },
          };
        },
      ],
    },
  ],
}

审计日志记录

记录所有工具调用以供审计,但不影响执行:

import { createCortexCodeSession } from "cortex-code-agent-sdk";
import { appendFileSync } from "node:fs";

const session = await createCortexCodeSession({
  cwd: process.cwd(),
  hooks: {
    PostToolUse: [
      {
        hooks: [
          async (input) => {
            const entry = {
              timestamp: new Date().toISOString(),
              tool: input.tool_name,
              input: input.tool_input,
              sessionId: input.session_id,
            };
            appendFileSync("audit.log", JSON.stringify(entry) + "\n");
            return {};
          },
        ],
      },
    ],
  },
});

Hook 与 canUseTool

Hook 和 canUseTool 回调都可以拦截工具调用,但它们的目的不同:

功能

canUseTool

Hook

范围

仅限执行前权限检查

多个生命周期事件(工具生命周期、提示提交、停止、子代理生命周期、通知和压缩)

事件

单一事件:权限请求

十个事件:PreToolUsePostToolUsePostToolUseFailurePermissionRequestUserPromptSubmitStopSubagentStartSubagentStopNotificationPreCompact

模式匹配

不支持匹配器。当规则列表无法解析权限请求时触发。

是(正则表达式匹配器根据事件按工具名称、通知类型或压缩触发器进行筛选)

修改输入

有限制。updatedInput 当前用于 SDK 路由的伪工具,例如 AskUserQuestionExitPlanMode

是 (hookSpecificOutput.updatedInput)

最适合用于

简单的允许/拒绝决策

审计日志记录、上下文注入、复杂策略、执行后操作

您可以同时使用两者。canUseTool 回调作为 CLI 权限系统的一部分运行,而 PreToolUse Hook 则单独运行。