kimi-code/docs/zh/customization/hooks.md
qer b253a82a7a
feat(agent-core): add Interrupt hook for user-interrupted turns (#607)
Fires an observation-only Interrupt event when a turn is aborted by the user (e.g. pressing Esc). Previously neither Stop nor StopFailure fired on interrupt, so external tooling that tracks status from hooks stayed stuck on a working state.
2026-06-10 15:35:25 +08:00

7.6 KiB
Raw Blame History

Hooks

Hooks钩子是一种自动触发机制你预先告诉 Kimi Code CLI"每当发生 X运行这个脚本"。脚本在你的本机执行,你可以在里面写任何逻辑。典型的使用场景:

  • 安全拦截Agent 要执行 Shell 命令前,检查是否包含危险操作(如 rm -rf),包含则阻断执行
  • 桌面通知:后台任务完成时,弹出系统通知提醒你回来查看结果
  • 自动检查:每次用户提交消息时,自动在上下文里附加一些背景信息(如当前 Git 分支)

Hooks 是怎么工作的

配置一条 hook 规则,需要指定三件事:在什么事件上触发匹配哪些目标运行哪个脚本

触发时CLI 会把事件的详细信息(触发原因、工具名称、命令内容等)打包成 JSON一种结构化文本格式通过标准输入stdin程序运行时用来接收外部数据的通道传给你的脚本。脚本读取这些信息后决定怎么响应。

脚本的响应结果由两样东西决定:

  • 退出码exit code程序结束时向操作系统报告的状态数字0 表示放行,2 表示阻断,其他数字默认放行
  • 标准输出stdout就是你用 console.logprint 打印出来的内容):可以附带说明文字

即使脚本报错、超时CLI 也不会因此中断你的工作——这种"出错就放行"的设计叫 fail-open失败开放避免 hook 异常变成绊脚石。

::: warning 注意 正因为 fail-openHooks 适合做提醒和轻量拦截,但不应作为唯一的安全防线。对真正高风险的操作,仍需依赖权限审批和人工确认。 :::

快速上手:一个最简单的 hook

下面这条 hook 会在每次后台任务完成时在终端标题栏闪一下通知macOS 需要安装 terminal-notifier

# 写在 ~/.kimi-code/config.toml 里
[[hooks]]
event = "Notification"           # 触发时机:后台任务状态变化时
matcher = "task\\.completed"     # 只关心"已完成"的通知
command = "terminal-notifier -title Kimi -message 'Task done'"

保存配置、重开会话,下次后台任务完成时就会弹出通知。

配置

所有 hook 规则写在 ~/.kimi-code/config.toml[[hooks]] 数组里,每一项是一条规则:

字段 类型 必填 说明
event string 触发事件名,必须是下文「事件一览」表中的某一项
matcher string 用正则表达式(一种字符串匹配语法)过滤事件目标;不填则匹配全部
command string 触发时要运行的 Shell 命令
timeout integer 超时秒数,范围 1600默认 30 秒

[[hooks]] 只允许这四个字段,多写会导致配置文件加载失败。

同一事件匹配多条规则时,所有命中的 hook 并行运行;command 完全相同的多条规则只运行一次。

Hook 命令的工作目录是当前会话的项目目录。非 Windows 平台上hook 进程放在独立进程组里,超时时先发信号让它有机会善后,之后才强制终止。

事件数据格式

每次触发时CLI 都会把以下基础信息通过 stdin 传给脚本:

{
  "hook_event_name": "PreToolUse",
  "session_id": "session_abc",
  "cwd": "/path/to/project"
}

具体事件还会附带额外字段如工具名称、命令内容见下方事件一览。所有字段名使用下划线命名snake_case

返回值

脚本结束后CLI 根据退出码判断 hook 的意图:

退出码 含义 CLI 怎么处理
0 正常结束,放行 继续执行若标准输出stdout有内容可附加到上下文
2 主动阻断 停止当前操作错误输出stderrconsole.error 打印的内容)作为阻断原因
其他非零值 脚本出错 默认放行fail-open
超时或崩溃 脚本异常 默认放行fail-open

也可以通过标准输出返回一段 JSON 来阻断:

{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "请用 rg 代替 grep"
  }
}

::: info 哪些事件支持阻断? 只有可阻断事件PreToolUseStopUserPromptSubmit)的返回值会影响主流程。其余事件属于观察型事件——触发后即发即忘,不管脚本返回什么,主流程都不会改变。 :::

事件一览

事件 Matcher 匹配的是 会触发阻断? 说明
UserPromptSubmit 用户提交的文本内容 用户发送消息时触发;返回文本会附加到上下文;若阻断,本轮不调用模型
PreToolUse 工具名 工具调用前触发(权限检查前);阻断后工具不会执行
Stop 空字符串 模型准备结束本轮时触发;阻断后可追加一条消息让模型继续
PostToolUse 工具名 工具成功执行后触发(观察用)
PostToolUseFailure 工具名 工具失败或被阻断后触发(观察用)
PermissionRequest 工具名 即将等待用户审批前触发(观察用)
PermissionResult 工具名 审批结束后触发(观察用)
SessionStart startupresume 新会话启动或历史会话恢复后触发
SessionEnd exit 会话关闭后触发
SubagentStart 子 Agent 名称 子 Agent 开始运行前触发
SubagentStop 子 Agent 名称 子 Agent 成功完成后触发(观察用)
StopFailure 错误类型 本轮因错误失败后触发(观察用)
Interrupt 空字符串 用户中断本轮时触发(例如按下 Esc超时或其他程序性中断不会触发。中断时 Stop 不会触发由本事件替代。payload 含 reason 字段(观察用)
PreCompact manualauto 上下文压缩开始前触发;返回值被完全忽略
PostCompact manualauto 上下文压缩完成后触发(观察用)
Notification 通知类型(如 task.completed 后台任务状态变化时触发(观察用)

示例:阻断危险 Shell 命令

下面的 hook 在 Agent 调用 Bash 工具前检查命令内容,发现 rm -rf 就阻断:

[[hooks]]
event = "PreToolUse"
matcher = "Bash"
command = "node ~/.kimi-code/hooks/block-dangerous-bash.mjs"
timeout = 5
// block-dangerous-bash.mjs
// 从 stdin 读取 CLI 传来的事件数据
let input = '';
process.stdin.on('data', (chunk) => { input += chunk; });
process.stdin.on('end', () => {
  const payload = JSON.parse(input);         // 解析事件数据
  const command = payload.tool_input?.command ?? '';

  if (command.includes('rm -rf')) {
    // 通过 stderr 说明阻断原因,退出码 2 表示阻断
    console.error('检测到危险命令,已阻断');
    process.exit(2);
  }
  // 正常退出(退出码 0表示放行
});

阻断后Kimi Code CLI 会把阻断原因写回上下文,模型可以据此选择更安全的替代方案。

::: warning 注意 此示例仅演示阻断机制,不是生产级的安全解析器。真实场景更适合用白名单,或用专门的 Shell 解析器处理引号、变量展开和多段命令。 :::

下一步

  • 配置文件[[hooks]]config.toml 中的完整字段声明
  • Agent 与子 Agent — 利用 SubagentStop 事件在子 Agent 完成后触发通知