Hook 系统

2026-10-29   访问量:0

版本要求:本文档针对 CodeBuddy Agent SDK v0.1.0 及以上版本。

本文档介绍如何在 SDK 中使用 Hook 系统,在工具执行前后插入自定义逻辑。

概述

Hook 允许您在 CodeBuddy 的会话生命周期内插入自定义逻辑,实现:

工具调用前的校验和拦截

工具执行后的日志记录

用户提交内容的审查

会话开始/结束时的初始化和清理

worktree 创建与清理时的自定义流程

支持的事件

事件

触发时机

PreToolUse

工具执行前

PostToolUse

工具执行成功后

UserPromptSubmit

用户提交消息时

Stop

主 Agent 响应结束时

SubagentStop

子 Agent 结束时

PreCompact

上下文压缩前

WorktreeCreate

创建隔离 worktree 时

WorktreeRemove

删除隔离 worktree 时

unstable_Checkpoint

文件修改后自动创建检查点时

Hook 配置

通过 hooks 选项配置 Hook。每个事件可以有多个 matcher,每个 matcher 可以有多个 hook 回调。

基本结构

TypeScript

Python

import { query } from '@tencent-ai/agent-sdk';



const q = query({

 prompt: '帮我分析代码',

 options: {

   model: 'deepseek-v3.1',

   hooks: {

     PreToolUse: [

       {

         matcher: 'Bash',  // 只匹配 Bash 工具

         hooks: [

           async (input, toolUseId, ctx) => {

             console.log('即将执行:', input);

             return { continue: true };

           }

         ],

         timeout:5000  // 超时时间(毫秒)

       }

     ]

   }

 }

});

HookMatcher 结构

字段

类型

说明

matcher

string

匹配模式,支持正则表达式。* 或空字符串匹配所有

hooks

HookCallback[]

回调函数数组

timeout

number

超时时间(TypeScript 毫秒,Python 秒)

Matcher 模式

精确匹配:"Bash" 只匹配 Bash 工具

正则匹配:"Edit|Write" 匹配 Edit 或 Write

通配符:"*" 或 "" 匹配所有工具

前缀匹配:"mcp__.*" 匹配所有 MCP 工具

事件类型

PreToolUse

工具执行前触发,可以阻止执行或修改输入。

TypeScript

Python

hooks: {

 PreToolUse: [{

   matcher: 'Bash',

   hooks: [

     async (input, toolUseId, ctx) => {

       const command = input.command as string;



       // 阻止危险命令

       if (command.includes('rm -rf')) {

         return {

           decision: 'block',

           reason: '危险命令被阻止'

         };

       }



       return { continue: true };

     }

   ]

 }]

}

PostToolUse

工具执行成功后触发,可以添加额外上下文。

TypeScript

Python

hooks: {  PostToolUse: [{    matcher: 'Write|Edit',    hooks: [      async (input, toolUseId) => {        console.log(`文件已修改: ${input.file_path}`);        // 记录修改日志        await logFileChange(input.file_path);        return { continue: true };      }    ]  }]}

UserPromptSubmit

用户提交消息时触发,可以添加上下文或阻止处理。

TypeScript

Python

hooks: {

 UserPromptSubmit: [{

   hooks: [

     async (input) => {

       const prompt = input.prompt as string;



       // 敏感词检查

       if (containsSensitiveWords(prompt)) {

         return {

           decision: 'block',

           reason: '消息包含敏感内容'

         };

       }



       return { continue: true };

     }

   ]

 }]

}

Stop / SubagentStop

Agent 响应结束时触发,可以阻止停止并要求继续。

TypeScript

Python

hooks: {  Stop: [{    hooks: [      async (input) => {        // 检查任务是否真正完成        if (!isTaskComplete()) {          return {            decision: 'block',            reason: '任务未完成,请继续'          };        }        return { continue: true };      }    ]  }]}

unstable_Checkpoint(实验性)

文件修改后(Write/Edit/MultiEdit 工具执行成功)自动触发,提供文件快照和变更统计信息。

实验性 API

此 Hook 为实验性功能,API 可能在未来版本中发生变化。

TypeScript

Python

import type { CheckpointHookInput } from '@tencent-ai/agent-sdk';



hooks: {

 unstable_Checkpoint: [{

   hooks: [

     async (input) => {

       const checkpointInput = input as CheckpointHookInput;

       const checkpoint = checkpointInput.checkpoint;

       

       console.log('文件变更检查点:', {

         id: checkpoint.id,

         label: checkpoint.label,

         files: checkpoint.fileChangeStats?.files,

         additions: checkpoint.fileChangeStats?.additions,

         deletions: checkpoint.fileChangeStats?.deletions

       });

       

       // 访问文件快照

       for (const [filePath, version] of Object.entries(checkpoint.fileSnapshots)) {

         console.log(`  ${filePath} - 版本 ${version.version}`);

       }

       

       return { continue: true };

     }

   ]

 }]

}

Checkpoint 数据结构:

id: 检查点唯一标识符

label: 人类可读标签(通常为用户提示)

createdAt: 创建时间戳

fileSnapshots: 文件路径到版本信息的映射

filePath: 文件绝对路径

version: 版本号

backupFileName: 备份文件名

backupTime: 备份时间戳

fileChangeStats: 文件变更统计

files: 变更的文件路径列表

additions: 新增行数

deletions: 删除行数

Hook 输入

Hook 回调接收的输入结构因事件类型而异。

公共字段

{  "session_id": "abc123",  "cwd": "/path/to/project",  "permission_mode": "default",  "hook_event_name": "PreToolUse"}

PreToolUse / PostToolUse 输入

{  "tool_name": "Bash",  "tool_input": {    "command": "ls -la"  }}

UserPromptSubmit 输入

{  "prompt": "帮我写一个函数"}

Stop / SubagentStop 输入

{  "stop_hook_active": false}

WorktreeCreate 输入示例:

{  "hook_event_name": "WorktreeCreate",  "session_id": "abc123",  "cwd": "/path/to/project",  "transcript_path": "/path/to/transcript.jsonl",  "name": "feature-auth"}

WorktreeRemove 输入示例:

{  "hook_event_name": "WorktreeRemove",  "session_id": "abc123",  "cwd": "/path/to/project",  "transcript_path": "/path/to/transcript.jsonl",  "worktree_path": "/tmp/codebuddy-worktrees/feature-auth"}

unstable_Checkpoint 输入

{  "checkpoint": {    "id": "ckpt_abc123",    "label": "帮我写一个函数",    "createdAt": 1705920000000,    "fileSnapshots": {      "/path/to/file.ts": {        "filePath": "/path/to/file.ts",        "version": 1,        "backupFileName": "file.ts.v1.backup",        "backupTime": 1705920000000      }    },    "fileChangeStats": {      "files": ["/path/to/file.ts"],      "additions": 10,      "deletions": 2    }  }}

Hook 输出

Hook 回调返回的输出控制后续行为。

基本输出字段

字段

类型

说明

continue / continue_

boolean

是否继续执行(默认 true)

decision

'block'

设为 'block' 阻止操作

reason

string

阻止原因

stopReason

string

当 continue 为 false 时显示的停止消息

suppressOutput

boolean

隐藏输出

PreToolUse 特殊输出

可以修改工具输入:

TypeScript

Python

return {  continue: true,  hookSpecificOutput: {    hookEventName: 'PreToolUse',    updatedInput: {      command: `echo "安全检查通过" && ${input.command}`    }  }};

PostToolUse 特殊输出

可以向 Agent 追加上下文(additionalContext),或用 updatedToolOutput替换将要发送给 Agent 的工具结果(对所有工具生效,常用于压缩冗长输出以节省 token):

TypeScript

Python

return {  continue: true,  hookSpecificOutput: {    hookEventName: 'PostToolUse',    // 替换工具结果(结果可变短);也可用 additionalContext 追加(结果只会变长)    updatedToolOutput: compress(input.tool_response)  }};

示例

完整示例:Bash 命令审计

TypeScript

Python

import { query } from '@tencent-ai/agent-sdk';

import * as fs from 'fs';



const logFile = '/tmp/bash-audit.log';



const q = query({

 prompt: '帮我清理临时文件',

 options: {

   model: 'deepseek-v3.1',

   hooks: {

     PreToolUse: [{

       matcher: 'Bash',

       hooks: [

         async (input, toolUseId) => {

           const command = input.command as string;

           const timestamp = new Date().toISOString();



           // 记录命令

           fs.appendFileSync(logFile, `${timestamp} [PRE] ${command}\n`);



           // 危险命令检查

           const dangerous = ['rm -rf /', 'mkfs', ':(){:|:&};:'];

           for (const d of dangerous) {

             if (command.includes(d)) {

               return {

                 decision: 'block',

                 reason: `危险命令被阻止: ${d}`

               };

             }

           }



           return { continue: true };

         }

       ]

     }],

     PostToolUse: [{

       matcher: 'Bash',

       hooks: [

         async (input, toolUseId) => {

           const command = input.command as string;

           const timestamp = new Date().toISOString();



           // 记录执行完成

           fs.appendFileSync(logFile, `${timestamp} [POST] ${command} - 完成\n`);



           return { continue: true };

         }

       ]

     }]

   }

 }

});



for await (const message of q) {

 console.log(message);

}

示例:限制文件修改范围

TypeScript

Python

hooks: {

 PreToolUse: [{

   matcher: 'Write|Edit',

   hooks: [

     async (input) => {

       const filePath = input.file_path as string;



       // 只允许修改 src 目录

       if (!filePath.startsWith('/path/to/project/src/')) {

         return {

           decision: 'block',

           reason: `不允许修改 src 目录外的文件: ${filePath}`

         };

       }



       // 禁止修改配置文件

       if (filePath.endsWith('.env') || filePath.includes('.git/')) {

         return {

           decision: 'block',

           reason: '不允许修改敏感文件'

         };

       }



       return { continue: true };

     }

   ]

 }]

}

示例:文件修改追踪(Checkpoint Hook)

TypeScript

Python

import { query, type CheckpointHookInput } from '@tencent-ai/agent-sdk';

import * as fs from 'fs';



const changeLog = '/tmp/file-changes.log';



const q = query({

 prompt: '重构 src/utils.ts 文件',

 options: {

   model: 'deepseek-v3.1',

   hooks: {

     unstable_Checkpoint: [{

       hooks: [

         async (input) => {

           const checkpointInput = input as CheckpointHookInput;

           const checkpoint = checkpointInput.checkpoint;

           const stats = checkpoint.fileChangeStats;

           

           if (!stats) return { continue: true };

           

           // 记录文件变更

           const timestamp = new Date().toISOString();

           const logEntry = `

[${timestamp}] Checkpoint ${checkpoint.id}

 Label: ${checkpoint.label}

 Files: ${stats.files.join(', ')}

 Changes: +${stats.additions}/-${stats.deletions}

 Snapshots: ${Object.keys(checkpoint.fileSnapshots).length} files

`;

           

           fs.appendFileSync(changeLog, logEntry);

           

           // 如果变更过大,提醒用户

           if (stats.additions + stats.deletions > 100) {

             console.warn('大量代码变更,建议review');

           }

           

           return { continue: true };

         }

       ]

     }]

   }

 }

});



for await (const message of q) {

 console.log(message);

}

相关文档

SDK 概览 - 快速入门和使用示例

SDK 权限控制 - canUseTool 回调

Hook 参考指南 - CLI Hook 完整参考

TypeScript SDK 参考 - 完整 API 参考

Python SDK 参考 - 完整 API 参考


热门文章
更多>