版本要求:本文档针对 CodeBuddy Agent SDK v0.1.0 及以上版本。
本文档介绍如何在 SDK 中使用 Hook 系统,在工具执行前后插入自定义逻辑。
Hook 允许您在 CodeBuddy 的会话生命周期内插入自定义逻辑,实现:
工具调用前的校验和拦截
工具执行后的日志记录
用户提交内容的审查
会话开始/结束时的初始化和清理
worktree 创建与清理时的自定义流程
事件 | 触发时机 |
| 工具执行前 |
| 工具执行成功后 |
| 用户提交消息时 |
| 主 Agent 响应结束时 |
| 子 Agent 结束时 |
| 上下文压缩前 |
| 创建隔离 |
| 删除隔离 |
| 文件修改后自动创建检查点时 |
通过 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 // 超时时间(毫秒)
}
]
}
}
});
字段 | 类型 | 说明 |
|
| 匹配模式,支持正则表达式。 |
|
| 回调函数数组 |
|
| 超时时间(TypeScript 毫秒,Python 秒) |
精确匹配:"Bash" 只匹配 Bash 工具
正则匹配:"Edit|Write" 匹配 Edit 或 Write
通配符:"*" 或 "" 匹配所有工具
前缀匹配:"mcp__.*" 匹配所有 MCP 工具
工具执行前触发,可以阻止执行或修改输入。
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 };
}
]
}]
}
工具执行成功后触发,可以添加额外上下文。
TypeScript
Python
hooks: { PostToolUse: [{ matcher: 'Write|Edit', hooks: [ async (input, toolUseId) => { console.log(`文件已修改: ${input.file_path}`); // 记录修改日志 await logFileChange(input.file_path); return { continue: true }; } ] }]}
用户提交消息时触发,可以添加上下文或阻止处理。
TypeScript
Python
hooks: {
UserPromptSubmit: [{
hooks: [
async (input) => {
const prompt = input.prompt as string;
// 敏感词检查
if (containsSensitiveWords(prompt)) {
return {
decision: 'block',
reason: '消息包含敏感内容'
};
}
return { continue: true };
}
]
}]
}
Agent 响应结束时触发,可以阻止停止并要求继续。
TypeScript
Python
hooks: { Stop: [{ hooks: [ async (input) => { // 检查任务是否真正完成 if (!isTaskComplete()) { return { decision: 'block', reason: '任务未完成,请继续' }; } return { continue: true }; } ] }]}
文件修改后(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 回调接收的输入结构因事件类型而异。
{ "session_id": "abc123", "cwd": "/path/to/project", "permission_mode": "default", "hook_event_name": "PreToolUse"}
{ "tool_name": "Bash", "tool_input": { "command": "ls -la" }}
{ "prompt": "帮我写一个函数"}
{ "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"}
{ "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 回调返回的输出控制后续行为。
字段 | 类型 | 说明 |
|
| 是否继续执行(默认 true) |
|
| 设为 |
|
| 阻止原因 |
|
| 当 |
|
| 隐藏输出 |
可以修改工具输入:
TypeScript
Python
return { continue: true, hookSpecificOutput: { hookEventName: 'PreToolUse', updatedInput: { command: `echo "安全检查通过" && ${input.command}` } }};
可以向 Agent 追加上下文(additionalContext),或用 updatedToolOutput替换将要发送给 Agent 的工具结果(对所有工具生效,常用于压缩冗长输出以节省 token):
TypeScript
Python
return { continue: true, hookSpecificOutput: { hookEventName: 'PostToolUse', // 替换工具结果(结果可变短);也可用 additionalContext 追加(结果只会变长) updatedToolOutput: compress(input.tool_response) }};
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 };
}
]
}]
}
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 参考