介绍
挂钩是在会话期间的特定生命周期点执行的外部命令,可用于自定义自动化、安全控制和集成。
挂钩在两个 Copilot 界面中受支持:Copilot 命令行界面(CLI) 和 Copilot云代理。 大多数配置格式和事件有效负载相同,但执行环境和可以触发的事件集有所不同。
在本文中,两个界面之间行为不同的情况会通过“仅 CLI”和“仅云智能体”注释进行标注。 未标记的任何内容都适用于这两者。
挂钩位置
挂钩运行的位置以及存储挂钩配置文件的位置取决于表面:
-
Copilot 命令行界面(CLI) — 挂钩在开发人员的本地计算机上运行,其 shell 与 CLI 相同。 CLI 支持本文中所述的所有挂钩事件。
挂钩将按以下顺序(用户、项目、插件)从以下来源加载并合并: 当同一事件出现在多个源中时,将运行来自所有源的所有挂钩条目。
- 存储库级挂钩文件 -
.github/hooks/*.json在存储库根目录中。 - 用户级挂钩文件 -
*.json用户级挂钩目录中的文件。 默认情况下,macOS 和 Linux 上的~/.copilot/hooks/,或 Windows 上的%USERPROFILE%\.copilot\hooks\。 如果COPILOT_HOME已设置,则为$COPILOT_HOME/hooks/. -
存储库设置中的**内联 `hooks` 代码块** — 存储库中 `.github/copilot/settings.json`(已提交至 Git)或 `.github/copilot/settings.local.json`(通常被 Git 忽略且与用户相关)顶层中的 `hooks` 字段。 存储库中的跨工具 `.claude/settings.json` 和 `.claude/settings.local.json` 文件也会被读取。 -
用户级配置中的**内联 `hooks` 代码块** — 位于 `~/.copilot/settings.json` 顶层的 `hooks` 字段。 -
**已安装插件提供的挂钩** — 由每个插件在其安装目录内的 `hooks.json`(或 `hooks/hooks.json` 之下)中声明。
- 存储库级挂钩文件 -
-
Copilot云代理 — 挂钩在云代理为每个作业预配的临时 Linux 沙盒内运行。 沙盒是非交互式的,具有受约束的网络,并在作业结束时销毁。 仅触发部分事件,且仅处理
bash(或command)条目。挂钩配置被加载自克隆存储库中的
.github/hooks/*.json文件。
云代理执行环境
本节仅适用于Copilot云代理。 它描述了影响您编写挂钩脚本以及为云智能体任务配置挂钩条目的限制条件。
| 财产 | 值 |
|---|---|
| 操作系统 | Linux。 仅命令挂钩中的 bash 字段会被采纳;powershell 条目将被忽略。 跨平台 command 字段被视为备用方案。 |
| 工作目录 | |
/workspace 克隆存储库时,否则为 /root。 在挂钩条目上设置 cwd 或从脚本引用文件时使用此路径。 | |
| Filesystem | 短暂。 作业结束时,将丢弃由钩子写入的文件(例如日志、CSV 和转录文件)。 若要保留挂钩输出,请通过 http 挂钩条目发送它。 |
| 出站网络 | 受云代理防火墙限制。 默认情况下,只有GitHub和Copilot主机名可访问;访问任何其他主机(例如https://hooks.example.com)需要管理员配置的防火墙允许规则。 |
| 可用的环境变量 | |
GITHUB_COPILOT_API_TOKEN 和 GITHUB_COPILOT_GIT_TOKEN 被设置在沙盒中。 COPILOT_AGENT_PROMPT 包含调用该任务时的提示信息。 | |
HOME 被设置为 /root,因此任何解析 ~/... 路径的挂钩脚本会写入到临时沙箱中。 | |
GITHUB_TOKEN 未设置。 | |
| 交互性 | 完全非交互式。 代理使用预先授予的所有工具权限运行,因此不会显示任何权限对话框,并且不会向用户显示任何通知。 |
| 配置发现 | 在云代理作业中,默认存在的唯一挂钩配置位于 .github/hooks/*.json 克隆的存储库中。 沙盒中不包含用户级别的挂钩文件、settings.json、config.json 或已安装的插件。 |
钩子配置格式
挂钩配置文件使用 JSON 格式和版本 1。
命令挂钩
命令挂钩运行 shell 脚本,在所有挂钩类型上都受支持。
注意
仅限云代理。 云代理在 Linux 沙盒中运行挂钩。 仅采纳bash字段;powershell条目将被忽略。 跨平台 command 字段被视为备用方案。
{
"version": 1,
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "your-bash-command",
"powershell": "your-powershell-command",
"cwd": "optional/working/directory",
"env": { "VAR": "value" },
"timeoutSec": 30
}
]
}
}
| 领域 | 类型 | 必需 | Description |
|---|---|---|---|
bash | 字符串 | ||
bash、powershell或 command 之一 | Unix 的 Shell 命令。 | ||
command | 字符串 | ||
bash、powershell或 command 之一 | 当当前平台未设置 bash 或 powershell 时,将使用跨平台备用方案。 | ||
cwd | 字符串 | 否 | 命令的工作目录(相对于存储库根目录或绝对目录)。 |
env | 对象 | 否 | 要设置的环境变量(支持变量扩展)。 |
powershell | 字符串 | ||
bash、powershell或 command 之一 | 适用于 Windows 的 Shell 命令。 | ||
timeoutSec | number | 否 | 超时(以秒为单位)。 默认值:30。 |
type | "command" | 是的 | 必须是 "command"。 |
HTTP 钩子
HTTP 挂钩将输入有效负载作为 JSON POST 发送到 URL。
注意
仅限云代理。 来自沙盒的出站网络受云代理防火墙的限制,因此 url 必须面向在允许列表中的主机。
{
"version": 1,
"hooks": {
"postToolUse": [
{
"type": "http",
"url": "https://hooks.example.com/copilot",
"headers": { "X-Source": "copilot-cli" },
"allowedEnvVars": ["GITHUB_TOKEN"],
"timeoutSec": 30
}
]
}
}
| 领域 | 类型 | 必需 | Description |
|---|---|---|---|
allowedEnvVars | string[] | 否 | 环境变量名称可在 headers 值内扩展。 设置时, url 必须使用 https://。 |
headers | 对象 | 否 | 要包含的请求头。 |
timeoutSec | number | 否 | 超时(以秒为单位)。 默认值:30。 |
type | "http" | 是的 | 必须是 "http"。 |
url | 字符串 | 是的 | 目标 URL。 必须使用http:或https:。 对于 preToolUse 和 permissionRequest,必须使用 https:// ,因为响应可以授予工具权限。 |
提示挂钩
提示挂钩自动提交文本,就像用户键入文本一样。 它们仅受支持于sessionStart。 文本可以是自然语言提示或斜杠命令。
注意
Copilot 命令行界面(CLI) 仅限于此。 提示挂钩仅在新的交互式会话时触发。 它们不会在恢复时触发,也不会在非交互式提示模式 (-p) 下触发。
注意
云代理。 云代理作业以非交互方式运行(类似于 -p),因此 prompt 挂钩条目可能不会触发。 在依赖它们之前,请确认它们在您的环境中的行为。
{
"version": 1,
"hooks": {
"sessionStart": [
{
"type": "prompt",
"prompt": "Your prompt text or /slash-command"
}
]
}
}
| 领域 | 类型 | 必需 | Description |
|---|---|---|---|
type | "prompt" | 是的 | 必须是 "prompt"。 |
prompt | 字符串 | 是的 | 要提交的文本可以是自然语言消息或斜杠命令。 |
挂钩事件
下表列出了每个受支持的事件。 “云代理”列显示事件是否在云代理下触发,并记录任何行为差异。
| 事件 | 在以下情况下触发 | 已处理的输出 | 云代理 |
|---|---|---|---|
agentStop | 主要代理完成一回合。 | 是 — 可以阻止和强制继续。 | 火灾。 decision: "block" 强制进行另一轮操作,这仍计入作业的超时时间。 |
errorOccurred | 执行期间发生错误。 | 否 | 火灾。 |
notification | 当 CLI 发出系统通知(shell 完成、代理完成或空闲、权限提示、启发对话框)时异步触发。 即发即弃:从不阻止会话。 支持 matcher 正则表达式适用于 notification_type. | 可选:可以将 additionalContext 注入到会话中。 | |
| 不触发。 云代理不会向用户显示通知(请参阅上面的云代理执行环境表中的 交互 行)。 | |||
permissionRequest | 在权限服务运行之前触发(规则引擎、会话审批、自动允许/自动拒绝和用户提示)。 如果合并挂钩输出返回 behavior: "allow" 或 "deny",该决策会使正常权限流短路。 支持 matcher 正则表达式适用于 toolName. | 是 — 可以以编程方式允许或拒绝。 | 工具调用已预先批准,因此此挂钩要么不触发,要么无效。 改为使用 preToolUse 来做权限决策。 |
postToolUse | 每个工具成功完成操作后。 | 是 — 可以修改工具结果或为模型注入其他上下文。 | 火灾。 |
postToolUseFailure | 工具以失败告终后。 | 是 - 可以通过 additionalContext(命令挂钩的退出代码 2)提供恢复指导。 | 火灾。 |
preCompact | 上下文压缩即将开始(手动或自动)。 支持 matcher 按触发器("manual" 或 "auto") 进行筛选。 | 否 - 仅通知。 | 仅在满足 trigger: "auto" 条件时触发。 没有用户请求手动压缩。 |
preToolUse | 在每个工具执行之前。 | 是 — 可以允许、拒绝或修改。 | 火灾。 由于没有用户可以回答,"ask" 的决策被视为 "deny"。 |
sessionEnd | 会话终止。 | 否 | 每个作业触发一次。 |
reason 通常是 "complete", "error"或 "timeout"; "abort" ,并且 "user_exit" 不需要,因为没有用户。 | |||
sessionStart | 新的或已恢复的会话开始。 | 可选:可以将 additionalContext 注入到会话中。 | 每个任务仅触发一次,作为新会话(而非恢复)。 有关云智能体下 prompt 条目的行为,请参阅上文的“提示挂钩”说明。 |
subagentStart | 子代理在运行之前生成。 支持 matcher 按代理名称进行筛选。 | 可选 — 无法阻止创建,但 additionalContext 会作为前缀添加到子智能体的提示中。 | 火灾。 |
subagentStop | 子代理完成。 | 是 — 可以阻止和强制继续。 | 火灾。 |
userPromptSubmitted | 用户提交提示。 | 否 | 最多触发一次,针对提供给该任务的提示。 没有后续用户输入。 |
挂钩事件输入有效负载
每个挂钩事件将 JSON 有效负载传递到挂钩处理程序。 支持两种负载格式,可根据挂钩配置中使用的事件名称进行选择。
- camelCase 格式 - 在 camelCase 中配置事件名称(例如)。
sessionStart字段使用 camelCase。 - VS Code兼容格式 - 在 PascalCase 中配置事件名称(例如)。
SessionStart字段使用 snake_case 来匹配 VS CodeCopilot 扩展格式。
sessionStart / SessionStart
camelCase 输入:
{
sessionId: string;
timestamp: number; // Unix timestamp in milliseconds
cwd: string;
source: "startup" | "resume" | "new";
initialPrompt?: string;
}
** VS Code 兼容的输入:**
{
hook_event_name: "SessionStart";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
source: "startup" | "resume" | "new";
initial_prompt?: string;
}
sessionEnd / SessionEnd
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
reason: "complete" | "error" | "abort" | "timeout" | "user_exit";
}
** VS Code 兼容的输入:**
{
hook_event_name: "SessionEnd";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
reason: "complete" | "error" | "abort" | "timeout" | "user_exit";
}
userPromptSubmitted / UserPromptSubmit
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
prompt: string;
}
** VS Code 兼容的输入:**
{
hook_event_name: "UserPromptSubmit";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
prompt: string;
}
preToolUse / PreToolUse
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
toolName: string;
toolArgs: unknown;
}
** VS Code 兼容的输入:**
配置了 PascalCase 事件名称 PreToolUse 后,有效负载会使用 snake_case 字段名称来匹配 VS CodeCopilot 扩展格式:
{
hook_event_name: "PreToolUse";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
tool_name: string;
tool_input: unknown; // Tool arguments (parsed from JSON string when possible)
}
postToolUse / PostToolUse
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
toolName: string;
toolArgs: unknown;
toolResult: {
resultType: "success";
textResultForLlm: string;
}
}
** VS Code 兼容的输入:**
{
hook_event_name: "PostToolUse";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
tool_name: string;
tool_input: unknown;
tool_result: {
result_type: "success";
text_result_for_llm: string;
}
}
postToolUseFailure / PostToolUseFailure
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
toolName: string;
toolArgs: unknown;
error: string;
}
** VS Code 兼容的输入:**
{
hook_event_name: "PostToolUseFailure";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
tool_name: string;
tool_input: unknown;
error: string;
}
agentStop / Stop
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
stopReason: "end_turn";
}
** VS Code 兼容的输入:**
{
hook_event_name: "Stop";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
transcript_path: string;
stop_reason: "end_turn";
}
subagentStart
输入:
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
agentName: string;
agentDisplayName?: string;
agentDescription?: string;
}
subagentStop / SubagentStop
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
agentName: string;
agentDisplayName?: string;
stopReason: "end_turn";
}
** VS Code 兼容的输入:**
{
hook_event_name: "SubagentStop";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
transcript_path: string;
agent_name: string;
agent_display_name?: string;
stop_reason: "end_turn";
}
errorOccurred / ErrorOccurred
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
error: {
message: string;
name: string;
stack?: string;
};
errorContext: "model_call" | "tool_execution" | "system" | "user_input";
recoverable: boolean;
}
** VS Code 兼容的输入:**
{
hook_event_name: "ErrorOccurred";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
error: {
message: string;
name: string;
stack?: string;
};
error_context: "model_call" | "tool_execution" | "system" | "user_input";
recoverable: boolean;
}
preCompact / PreCompact
camelCase 输入:
{
sessionId: string;
timestamp: number;
cwd: string;
transcriptPath: string;
trigger: "manual" | "auto";
customInstructions: string;
}
** VS Code 兼容的输入:**
{
hook_event_name: "PreCompact";
session_id: string;
timestamp: string; // ISO 8601 timestamp
cwd: string;
transcript_path: string;
trigger: "manual" | "auto";
custom_instructions: string;
}
preToolUse 决策控制
挂钩 preToolUse 可以通过将 JSON 对象写入 stdout 来控制工具执行。
| 领域 | 价值观 | Description |
|---|---|---|
permissionDecision | ||
"allow"、"deny"、"ask" | 工具是否已执行? 空输出使用默认行为。 在云代理下,由于没有用户可以回答,"ask" 被视为 "deny"。 | |
permissionDecisionReason | 字符串 | 向代理显示的原因。 决策为 "deny" 时需要。 |
modifiedArgs | 对象 | 要使用的替代工具参数,而不是使用原始参数。 |
agentStop/ subagentStop 决策控制
| 领域 | 价值观 | Description |
|---|---|---|
decision | ||
"block"、"allow" | "block" 强制另一个代理回合将 reason 用作提示。 | |
reason | 字符串 | 当decision是"block"时,提示下一轮。 |
postToolUse 输出
挂钩 postToolUse 可以通过将 JSON 对象写入 stdout 来修改工具结果或为模型注入其他上下文。
{
modifiedResult?: {
resultType: "success";
textResultForLlm: string;
};
additionalContext?: string;
}
| 领域 | 类型 | Description |
|---|---|---|
modifiedResult | 对象 | 替换工具的结果。 必须具有 resultType: "success"。 如果返回了 resultType: "failure",则故障会向下游传递,接下来会触发 postToolUseFailure。 |
additionalContext | 字符串 | 在 textResultForLlm 后附加额外指导,以便模型在同一轮次中看到它,且位于工具输出之后。 当多个钩子返回 additionalContext 时,结果会用两个换行符连接,总长度上限为 10 KB。 |
返回 {} 或清空输出以保留原始成功结果。
注意
modifiedResult 同时被 SDK 编程挂钩和命令/HTTP 配置文件 postToolUse 挂钩支持。
permissionRequest 决策控制
注意
Copilot 命令行界面(CLI) 仅限于此。 permissionRequest挂钩不适用于Copilot云代理,工具调用已获得预先批准(请参阅云代理执行环境表中交互行)。 使用preToolUse在云代理中进行权限决策。
该 permissionRequest 挂钩在权限服务运行前触发—在规则检查、会话审批、自动允许/自动拒绝和用户提示之前触发。 如果挂钩返回 behavior: "allow" 或 "deny",该决策会使正常权限流短路。 不返回任何结果时,会回退到正常权限处理。 使用它以编程方式批准或拒绝工具调用-特别适用于 CLI 管道模式(-p)和其他 CLI CI 用法,其中没有交互式提示可用。 它不适用于云代理。
所有已配置的 permissionRequest 挂钩均为每个请求运行(read 和 hook 权限类型除外,这两类在挂钩运行前短路)。 挂钩输出将合并,且后面的挂钩输出会覆盖前面的输出。
匹配器: 可选正则表达式针对 toolName 进行测试。 锚定为 ^(?:pattern)$;必须与完整的工具名称匹配。 设置后,挂钩仅针对匹配的工具名称触发。
将 JSON 输出到 stdout 以控制权限决策:
| 领域 | 价值观 | Description |
|---|---|---|
behavior | ||
"allow"、"deny" | 是否批准或拒绝工具调用请求。 | |
message | 字符串 | 拒绝时,原因会反馈给 LLM。 |
interrupt | boolean | 当 true 与 "deny" 结合后,会完全停止代理。 |
返回空输出或 {} 以进入到正常权限流。 对于命令钩子,退出代码 2 被视为拒绝执行,stdout JSON(如果有)与 {"behavior":"deny"} 合并,而 stderr 会被忽略。
notification 挂钩
注意
Copilot 命令行界面(CLI) 仅限于此。 notification 挂钩在 Copilot云代理 下不会触发。
当 notification CLI 发出系统通知时,挂钩会异步触发。 这些挂钩是即发即弃型挂钩:它们永远不会阻止会话,并且会记录并跳过任何错误。
输入:
{
sessionId: string;
timestamp: number;
cwd: string;
hook_event_name: "Notification";
message: string; // Human-readable notification text
title?: string; // Short title (e.g., "Permission needed", "Shell completed")
notification_type: string; // One of the types listed below
}
通知类型:
| 类型 | 当它触发时 |
|---|---|
shell_completed | 后台(异步)shell 命令完成 |
shell_detached_completed | 断开连接的 shell 会话完成 |
agent_completed | 后台子智能体完成(已完成或失败) |
agent_idle | 后台代理完成一个轮次并进入空闲状态(正在等待 write_agent) |
permission_prompt | 代理请求执行工具的权限 |
elicitation_dialog | 代理从用户请求其他信息 |
Output:
{
additionalContext?: string; // Injected into the session as a user message
}
如果 additionalContext 返回,文本将作为追加的用户消息注入到会话中。 如果会话处于空闲状态,可能会触发进一步的代理处理。 返回 {} 或空输出以不执行任何操作。
匹配器: 在 notification_type 上可选的正则表达式。 该模式被定位为 ^(?:pattern)$。 省略 matcher 以接收所有通知类型。
匹配器筛选
多个事件允许在每个挂钩条目中使用可选的 matcher 正则表达式,用于过滤挂钩将触发的调用。 该模式定位为^(?:matcher)$,并且必须与完整值匹配。 无效的正则表达式将导致跳过该挂钩条目。
| 事件 | matcher 匹配 |
|---|---|
notification | notification_type |
permissionRequest | toolName |
preCompact | |
trigger("manual" 或 "auto") | |
preToolUse | toolName |
subagentStart | agentName |
挂钩匹配工具名称
| 工具名称 | Description |
|---|---|
ask_user | 询问用户一个澄清的问题。 在云代理下,没有用户,因此 ask_user 不会产生有用的结果。 |
bash | 执行 shell 命令(Unix)。 |
create | 创建新文件。 |
edit | 修改文件内容。 |
glob | 按模式查找文件。 |
grep | 搜索文件内容。 |
powershell | 执行 shell 命令(Windows)。 不会显示在云代理(Linux 沙盒)下。 |
task | 运行子代理任务。 |
view | 读取文件内容。 |
web_fetch | 抓取网页。 |
如果配置了同一类型的多个挂钩,则它们按顺序执行。 对于 preToolUse,如果有挂钩返回 "deny",则该工具会被阻止。 挂钩失败(除 2 以外的非零退出代码,或超时)会被记录并跳过— 它们绝不会阻塞智能体的执行。
命令挂钩的退出代码
| 退出代码 | Meaning |
|---|---|
0 | 成功。 |
stdout 如果存在,则分析为挂钩输出 JSON。 | |
2 | 默认情况下被视为警告。 stderr 会显示给用户,但运行会继续。 对于 permissionRequest,退出 2 将被视为 {"behavior":"deny"},并且任何 stdout JSON 都会被合并进来。 对于postToolUseFailure,退出2被视为additionalContext,并且stdout被追加到展示给代理的失败信息中。 |
| 其他非零 | 记录为挂钩失败。 运行继续 (fail-open)。 |
禁用所有挂钩
当您想在磁盘上保留挂钩配置但阻止其运行时,使用 disableAllHooks。例如:
- 在调试问题时,您希望确认挂钩是原因,但又不希望删除配置。
- 在敏感任务(代码审查、发布分支、处理密钥)期间暂停自动化,同时保留配置。 (仅限 Copilot 命令行界面(CLI)。)
- 将挂钩文件放入源代码控制中,参与者可通过在
settings.json存储库中设置选项在本地选择不使用。 (仅限 Copilot 命令行界面(CLI)。) - 在交互式会话期间暂时禁用运行缓慢或输出冗余的挂钩。 (仅限 Copilot 命令行界面(CLI)。)
在顶级设置 disableAllHooks 为 true 以跳过文件中的所有挂钩,同时不删除该文件。
{
"version": 1,
"disableAllHooks": false,
"hooks": {
"preToolUse": [ /* hook entries */ ]
}
}
行为取决于设置标志的位置:
-
**在单个 `.github/hooks/*.json` 文件** 内 — 仅跳过该文件中声明的挂钩。 Copilot 命令行界面(CLI) 和 Copilot云代理 均支持此设置。 -
** 在 `settings.json` 存储库的顶层 ** — 仅限 **Copilot 命令行界面(CLI)。** 该存储库中的所有会话都会跳过来自所有来源(存储库文件、用户文件、插件和内联挂钩块)的每个挂钩。 云代理不会加载 `settings.json`。