首页
/ VS Code Copilot Hooks(.json):Agent 会话的确定性生命周期自动化机制详解

VS Code Copilot Hooks(.json):Agent 会话的确定性生命周期自动化机制详解

2026-09-06 17:48:53作者:盛欣凯Ernestine

本文基于 VS Code 仓库中 Copilot 定制技能包内的 Hooks 参考文档(hooks.md)展开,系统讲解 GitHub Copilot Agent Hooks 的配置文件位置、事件类型、JSON 配置格式与输入输出契约,并结合 VS Code 源码中的解析器、JSON Schema 与发现机制,说明每条配置字段在运行时如何被加载、解析和执行,帮助你在团队级 Agent 策略(如拦截危险命令、强制校验、自动注入上下文)落地时做到"配置即可用、原理可追溯"。

1. Hooks 是什么:确定性执行 vs 指导性提示

Agent 的定制能力可以分为两大类。Copilot 定制技能(SKILL.md)给出了完整的决策矩阵:

定制原语 行为特征
Instructions / Prompts / Skills / Agents Guidance(指导性,非确定性)
Hooks Runtime enforcement(运行时强制)与确定性自动化

原文档的定位是:Hooks 是对 agent 会话的确定性生命周期自动化(deterministic lifecycle automation)——用于强制策略、自动化校验、注入运行时上下文。当行为必须被"保证"发生,而不是"希望"发生时,才应使用 hooks,例如:阻止危险命令、强制代码校验、自动注入上下文。

这一点与 instructions 的区别非常关键:instructions 依赖模型"听从"提示,是概率性的;hooks 则在 PreToolUsePostToolUse 等生命周期节点上真实地执行 shell 命令,命令的输出与退出码直接决定流程继续还是阻断,是确定性的。

2. Hook 配置文件的存放位置

原文档给出了四个标准位置:

路径 作用域
.github/hooks/*.json 工作区(团队共享,随仓库提交)
.claude/settings.local.json 工作区本地(不提交)
.claude/settings.json 工作区
~/.claude/settings.json 用户主目录

所有已配置位置中的 hooks 都会被收集并执行,工作区 hooks 与用户 hooks 互不覆盖。

从源码结构看,这一"收集而非覆盖"语义有明确实现:

  • 发现逻辑位于 sessionCustomizationDiscovery.ts,它声明了 hook 类文件的扫描位置,除文档所列路径外,还包含 .github/copilot/settings.json.github/copilot/settings.local.json 以及 .claude/settings.json.claude/settings.local.json;
  • 合并逻辑在 hookSchema.ts 的 mergeHooks 中:对每一种 hook 类型,附加的 hooks 数组被追加到基础数组之后(result[hookType] = baseArr ? [...baseArr, ...additionalArr] : additionalArr),即同事件下多个来源的命令按顺序全部执行,而非后者覆盖前者;
  • 测试 sessionCustomizationDiscovery.test.ts 验证了多来源并存的行为,例如工作区 .github/hooks/pre-tool.json 与用户目录 ~/.copilot/hooks/post-tool.json 同时被发现,且非 .json 文件(如 not-a-hook.md)会被忽略,.github/copilot/settings.local.json.claude/settings.local.json 中的 hooks 字段同样被识别为 hook 来源。

多根工作区的一个细节:从 sessionState.ts 中的注释可以推断,Copilot agent 只会应用"主工作目录"下的 hooks,因此当只有一个工作区文件夹在 .github/hooks/ 下带有 hooks 时,系统会自动固定该文件夹为主目录并隐藏目录选择器;当多个文件夹都带有 hooks 时,会弹出选择器让用户消歧。

此外,插件格式的 hooks 也有各自的位置约定,见 pluginParsers.ts:Copilot 插件使用根目录的 hooks.json,Claude 插件与 OpenPlugin 格式使用 hooks/hooks.json

3. Hook 事件类型

原文档列出的 8 个事件及其触发时机:

事件 触发时机
SessionStart 新 agent 会话的第一个 prompt
UserPromptSubmit 用户提交 prompt
PreToolUse 工具调用之前
PostToolUse 工具调用成功之后
PreCompact 上下文压缩之前
SubagentStart 子代理(subagent)启动
SubagentStop 子代理结束
Stop Agent 会话结束

源码中 hookTypes.ts 的 HookType 枚举定义了完整的 10 个事件:除了上述 8 个,还有 SessionEndErrorOccurredHOOKS_BY_TARGET 表(hookTypes.ts L30-L68)按目标区分了每个后端支持的事件集合:

  • VS Code 格式:恰好支持文档所列的 8 个 PascalCase 事件(SessionStart ... Stop);
  • Claude 格式:与 VS Code 相同(无 SessionEnd/ErrorOccurred);
  • GitHub Copilot CLI 格式:使用 camelCase 命名(sessionStartuserPromptSubmittedpreToolUse 等),且独有 sessionEnderrorOccurred;
  • 未指定 target 时:列出全部已知事件。

同时,HOOK_METADATA(同文件 L81-L122)为每个事件提供了本地化描述,例如 PreToolUse 为"Executed before the agent uses any tool",可直接对照原文档的 Trigger 列。

4. 配置格式

4.1 最小完整示例

原文档给出的标准配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "./scripts/validate-tool.sh",
        "timeout": 15
      }
    ]
  }
}

每个事件对应一个 hook 命令数组。源码中的 JSON Schema(hookSchema.ts 的 vscodeHookCommandSchema)进一步明确了约束:

  • type必填字段,且只能取值 "command";
  • commandwindowslinuxosx 中至少提供一个(错误提示:"At least one of 'command', 'windows', 'linux', or 'osx' must be specified");
  • 文件顶层 hooks 字段必填。

4.2 每个 hook 命令支持的字段

原文档列出:type(必须为 command)、command(默认)、windows/linux/osx(平台覆盖)、cwdenvtimeout。结合 HOOK_COMMAND_FIELD_DESCRIPTIONS 可补全每个字段的语义:

字段 说明
type 必须为 "command"
command 要执行的默认跨平台命令
windows / linux / osx 对应平台特定命令;在该平台运行时覆盖 command 字段
bash Linux 与 macOS 的 Bash 命令(兼容格式,解析时映射到 linux + osx)
powershell Windows 的 PowerShell 命令(解析时映射到 windows)
cwd 脚本工作目录,相对路径相对于仓库根目录,支持 ~ 展开
env 附加环境变量,与既有环境合并(值为字符串的对象)
timeout 最长执行时间(秒),默认 30
timeoutSec Copilot CLI 格式下使用,默认 10

平台覆盖的实际解析逻辑resolveEffectiveCommand 中:按当前操作系统选择 windows/osx/linux 字段,未命中则回退到 command,与调试配置 launch.json 的按平台配置处理思路一致。

cwd 的路径解析resolveHookCommand 中:先做 ~ 展开,绝对路径直接使用,相对路径则与 workspace root 拼接;未指定 cwd 时默认就是 workspace root。

格式自动识别:hookFileSchema(hookSchema.ts L237-L301)通过文件中是否存在数值型 version 字段来区分两种格式——带 version 的文件按 Copilot CLI 格式解析(camelCase 事件名 + bash/powershell/timeoutSec 字段),否则按 VS Code PascalCase 格式解析。编辑器内打开 hook 文件时还可使用内置的 Basic hook configuration 片段。

4.3 兼容 Claude 风格的嵌套写法

extractHookCommandsFromItem 表明,当 Claude 风格的条目被粘贴进来时也能被正确解析:

  • 直接命令对象:{ "type": "command", "command": "..." };
  • matcher 的嵌套结构:{ "matcher": "...", "hooks": [{ "type": "command", ... }] };
  • Claude 格式允许省略 type 字段,解析时会按 command 处理(normalizeForResolve)。

Claude 风格的 bash 字段会被同时映射到 linuxosx,powershell 映射到 windows(normalizeHookCommand)。

5. 输入 / 输出契约

Hooks 通过 stdin 接收 JSON,可以经 stdout 返回 JSON

  • 通用输出字段:continuestopReasonsystemMessage;
  • PreToolUse 的权限决策从 hookSpecificOutput.permissionDecision 读取,取值为 allow | ask | deny;
  • PostToolUse 的输出可用 decision: block 阻止后续处理。

原文档给出的 PreToolUse 示例输出:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "ask",
    "permissionDecisionReason": "Needs user confirmation"
  }
}

源码中 Claude 会话的 SDK 选项 claudeSdkOptions.ts 同样构造了 hookSpecificOutput 结构,可印证该契约是跨后端的通用约定。

退出码约定:

退出码 语义
0 成功
2 阻断性错误(blocking error)
其他值 产生非阻断性警告(non-blocking warnings)

也就是说,hook 脚本不必总是以 JSON 表达决策:直接 exit 2 即可阻断流程,exit 1 等其他非零值会给出警告但不中断。

6. 源码级执行链路:一份 hook 配置如何生效

将上述模块串起来,一条 PreToolUse hook 从磁盘文件到真正执行的链路(基于源码结构梳理)大致是:

  1. 发现:agent 会话启动时,会话定制发现逻辑扫描 .github/hooks/*.json.github/copilot/settings*.json.claude/settings*.json 等位置(sessionCustomizationDiscovery.ts),非 JSON 文件被忽略;
  2. 解析:各格式解析器(pluginParsers.ts)调用 parseHooksJson,逐条经 normalizeHookCommandresolveHookCommand 得到规范化的 IHookCommand 对象;
  3. 合并:不同来源的 hooks 按事件类型用 mergeHooks 追加合并,形成 ChatRequestHooks(按事件分组的命令表),传递给扩展宿主;
  4. 触发:agent 运行到对应生命周期节点时执行命令,平台覆盖(windows/linux/osx)按当前 OS 解析出实际执行的命令,退出码与 stdout JSON 决定继续、警告或阻断。

子代理内联 hooks:除了独立 .json 文件,hooks 还可以内联在自定义 agent 的 YAML frontmatter 中。parseSubagentHooksFromYaml 解析 hooks 属性,支持两种写法(直接命令列表,或 Claude 风格带 matcher 的嵌套结构),并按 agent 的 target 正确解析事件名。SKILL.md 也明确:"Hooks can be defined in standalone .json files or inline in custom agent frontmatter via the hooks attribute"。

7. 最佳实践与反模式

原文档给出的四条核心原则与三条反模式,是团队落地 hooks 时最值得遵守的约束:

核心原则

  1. 保持 hook 小而可审计(Keep hooks small and auditable)——脚本短小、逻辑单点,便于 code review;
  2. 校验并清洗 hook 输入(Validate and sanitize hook inputs)——stdin 收到的 JSON 应做结构校验,不要盲目信任;
  3. 脚本中不要硬编码密钥(Avoid hardcoded secrets in scripts);
  4. 团队策略用工作区 hooks,个人自动化用用户 hooks(Prefer workspace hooks for team policy, user hooks for personal automation)——这与第 2 节"多来源全部执行、互不覆盖"的机制配合,形成清晰的职责分层。

反模式

  • 运行长时间阻塞正常流程的 hook(会拖慢每次工具调用,timeout 默认 30 秒也意味着长任务会频繁触发超时);
  • 在普通 instructions 就足够的场景滥用 hooks(hooks 是强制手段,提示能满足的需求不要升级);
  • 允许 agent 在无审批控制的情况下编辑 hook 脚本——hook 脚本本身具备拦截工具调用的权力,若 agent 可自由修改它们,策略防线形同虚设,应把 hook 脚本纳入人工审批或 CI 校验。

8. 相关文件索引

内容 路径
本文档(原始参考) extensions/copilot/assets/prompts/skills/agent-customization/references/hooks.md
Agent 定制总览与决策矩阵 extensions/copilot/assets/prompts/skills/agent-customization/SKILL.md
事件枚举、按目标的格式映射与本地化元数据 src/vs/workbench/contrib/chat/common/promptSyntax/hookTypes.ts
JSON Schema、字段语义、平台覆盖与路径解析 src/vs/workbench/contrib/chat/common/promptSyntax/hookSchema.ts
会话级 hook 位置发现 src/vs/platform/agentHost/node/copilot/sessionCustomizationDiscovery.ts
插件格式与 hook 配置文件位置约定 src/vs/platform/agentPlugins/common/pluginParsers.ts
多来源发现行为测试 src/vs/platform/agentHost/test/node/sessionCustomizationDiscovery.test.ts
多根工作区主目录固定逻辑注释 src/vs/platform/agentHost/common/state/sessionState.ts
Claude 兼容性(hookSpecificOutput 等) src/vs/platform/agentHost/node/claude/claudeSdkOptions.ts

适用前提说明:本文以当前 VS Code 仓库(包含 Copilot agent host 与定制技能包)的实际实现为准。VS Code 格式仅支持 8 个 PascalCase 事件;SessionEndErrorOccurred 及 camelCase 事件名属于 GitHub Copilot CLI 格式,仅在带 version 字段的配置文件中生效。

登录后查看全文
热门项目推荐
相关项目推荐