注意
Copilot SDK 当前处于 技术预览版. 功能和可用性可能会发生更改。
在 onPreToolUse 工具执行 之前 调用挂钩。 使用它可执行以下操作:
- 批准或拒绝工具执行
- 修改工具参数
- 添加工具的上下文
- 取消对话中的工具输出
挂钩签名
import type { PreToolUseHookInput, HookInvocation, PreToolUseHookOutput } from "@github/copilot-sdk";
type PreToolUseHandler = (
input: PreToolUseHookInput,
invocation: HookInvocation
) => Promise<PreToolUseHookOutput | null | undefined>;
有关 Python、Go 和 .NET 中的挂钩签名,请参阅 github/copilot-sdk 存储库。
输入
| 领域 | 类型 | 说明 |
|---|---|---|
timestamp | number | 触发挂钩时的 Unix 时间戳 |
cwd | 字符串 | 当前工作目录 |
toolName | 字符串 | 要调用的工具的名称 |
toolArgs | 对象 | 传递给工具的参数 |
输出
返回 null 或 undefined 允许工具执行,无需更改。 否则,返回具有以下任何字段的对象。
| 领域 | 类型 | 说明 |
|---|---|---|
permissionDecision |
`"allow"`
\|
`"deny"`
\|
`"ask"`
| 是否允许工具调用 |
| permissionDecisionReason | 字符串 | 向用户显示的说明(对于拒绝/询问) |
| modifiedArgs | 对象 | 传递给工具的已修改参数 |
| additionalContext | 字符串 | 注入到会话中的额外上下文 |
| suppressOutput | 布尔 | 如果为 true,工具输出将不会显示在对话中 |
权限决策
| 决定 | 行为 |
|---|---|
"allow" | 工具正常执行 |
"deny" | 工具被阻止,原因会显示给用户。 |
"ask" | 系统会提示用户批准(交互模式) |
示例
允许所有工具(仅限日志记录)
const session = await client.createSession({
hooks: {
onPreToolUse: async (input, invocation) => {
console.log(
`[${invocation.sessionId}] `
+ `Calling ${input.toolName}`
);
console.log(
` Args: ${JSON.stringify(input.toolArgs)}`
);
return { permissionDecision: "allow" };
},
},
});
有关 Python、Go 和 .NET 中的示例,请参阅 github/copilot-sdk 存储库。
阻止特定工具
const BLOCKED_TOOLS = [
"shell", "bash", "write_file", "delete_file",
];
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (BLOCKED_TOOLS.includes(input.toolName)) {
return {
permissionDecision: "deny",
permissionDecisionReason:
`Tool '${input.toolName}' `
+ `is not permitted in this environment`,
};
}
return { permissionDecision: "allow" };
},
},
});
修改工具参数
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
// Add a default timeout to all shell commands
if (
input.toolName === "shell" && input.toolArgs
) {
const args = input.toolArgs as {
command: string;
timeout?: number;
};
return {
permissionDecision: "allow",
modifiedArgs: {
...args,
timeout: args.timeout ?? 30000,
},
};
}
return { permissionDecision: "allow" };
},
},
});
限制对特定目录的文件访问
const ALLOWED_DIRECTORIES = [
"/home/user/projects", "/tmp",
];
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (
input.toolName === "read_file"
|| input.toolName === "write_file"
) {
const args = input.toolArgs as {
path: string;
};
const isAllowed =
ALLOWED_DIRECTORIES.some((dir) =>
args.path.startsWith(dir)
);
if (!isAllowed) {
return {
permissionDecision: "deny",
permissionDecisionReason:
`Access to '${args.path}' `
+ `is not permitted. `
+ `Allowed directories: `
+ ALLOWED_DIRECTORIES.join(", "),
};
}
}
return { permissionDecision: "allow" };
},
},
});
取消详细工具输出
const VERBOSE_TOOLS = ["list_directory", "search_files"];
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
return {
permissionDecision: "allow",
suppressOutput: VERBOSE_TOOLS.includes(input.toolName),
};
},
},
});
根据工具添加上下文
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (input.toolName === "query_database") {
return {
permissionDecision: "allow",
additionalContext:
"Remember: This database uses "
+ "PostgreSQL syntax. "
+ "Always use parameterized queries.",
};
}
return { permissionDecision: "allow" };
},
},
});
最佳做法
-
**始终返回决定。** `null`返回允许该工具,但显式使用`{ permissionDecision: "allow" }`更清晰。 -
**提供有用的拒绝原因。** 拒绝时,请解释理由,以便用户了解发生了什么。 -
**请谨慎修改参数。** 确保修改后的参数维护工具的预期架构。 -
**请考虑性能。** 在每个工具调用之前,同步运行预工具钩子。 让他们保持速度。 -
**谨慎使用 `suppressOutput` 。** 取消输出意味着模型看不到结果,这可能会影响聊天质量。 -
**请注意敏感数据。** 工具参数和结果可能包含机密、文件路径或个人身份信息。 避免在生产环境中记录或公开此数据。
延伸阅读
-
[AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/quickstart) -
[AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/post-tool-use) -
[AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/error-handling)