Module Structure
Hooks 不是独立业务流程,而是在工具、权限、会话、Agent 控制流、上下文压缩和 MCP
交互处改变后续行为的横切分支;HookManager 绑定 Session 配置,HookExecutor 执行
Command、Prompt、Function 或 HTTP 后端并归并事件特定结果。
Directory Layout
packages/cli/src/hooks/— 配置、管理、匹配、执行、输出校验、信任与安全后端packages/cli/src/tools/execution/ToolExecutionHooks.ts— 工具执行管线的 Pre/Post 适配packages/cli/src/server/routes/hooks.ts— Web Session 开关与 trust/revoke APIpackages/cli/src/slash-commands/hooks.ts— TUI/ACP Hook 管理命令packages/cli/src/ui/components/HooksManager.tsx— TUI 配置与信任界面packages/cli/web/src/components/settings/HookTrustPanel.tsx— Web 信任审阅界面
Key Entry Points
HookManager.bindSessionConfig()inpackages/cli/src/hooks/HookManager.ts— 冻结并关联 source/worktree 的 Session 配置HookManager.executePreToolHooks()inpackages/cli/src/hooks/HookManager.ts— 工具前置决策与输入改写HookExecutor.executeHook()inpackages/cli/src/hooks/HookExecutor.ts— trust gate 与后端分派runPreToolUseHooks()inpackages/cli/src/tools/execution/ToolExecutionHooks.ts— 将 Hook 决策合并回工具准入HookTrustService.getStatus()inpackages/cli/src/hooks/HookTrustService.ts— 校验当前外部 Hook 摘要
Branching Table
| 分支维度 | 分支条件 | 实际行为 |
|---|---|---|
| 配置与宿主开关 | config disabled、进程禁用或 Session 暂停 | 所有事件返回各自的 pass-through 默认值,不调用 executor |
| Hook 类型信任 | Function Hook | 不进入 Hook Trust 摘要,可作为进程内宿主策略直接执行 |
| Hook 类型信任 | Command、Prompt、HTTP Hook | 仅 trusted 摘要执行;untrusted、modified、error 均跳过并给 warning |
| 工具权限模式 | Plan 的 PreToolUse/PostToolUse | 直接跳过这两个工具 Hook;其他生命周期事件不因 Plan 自动跳过 |
| 工具权限模式 | YOLO 的 PreToolUse | 保留 deny 和输入修改,把 ask 收敛为 allow |
| 执行顺序 | PreToolUse / PermissionRequest / Stop / Compaction | 串行执行,可短路或累积前序结果 |
| 执行顺序 | PostToolUse / PostToolUseFailure / SessionEnd | 受 maxConcurrentHooks 限制并行执行,再按配置顺序归并结果 |
| HTTP 目标 | 默认策略与 allowedHosts 命中 | 默认要求 HTTPS 且拒绝 loopback/私网/link-local;allowlist 命中会绕过这些限制 |
| 失败或超时 | behavior=ignore / ask / deny | 分别继续并警告、返回确认分支、或立即产生阻塞结果 |
Affected Scope
packages/cli/src/tools/execution/— 工具规则、输入重建、审批、成功/失败结果与后续诊断packages/cli/src/agent/runtime/SessionRuntime.ts— SessionStart 环境、配置快照、worktree 别名与 dispose 清理packages/cli/src/agent/loop/completionPolicy.ts— 主 Agent Stop Hook 的继续/停止决策packages/cli/src/tools/builtin/task/task.ts— SubagentStop 后续提示与再次执行packages/cli/src/context/CompactionService.ts— 手动/自动压缩前的阻断分支packages/cli/src/mcp/McpClient.ts— Elicitation 展示前和响应回传前的改写或取消packages/cli/src/services/GracefulShutdown.ts— 进程退出时触发 SessionEndpackages/cli/src/plugins/PluginIntegrator.ts— 插件 Hooks 的整批合并、来源标记与刷新恢复packages/cli/src/ui/— 用户提示提交、Session 级启停与 TUI 信任审阅packages/cli/web/src/components/settings/HookTrustPanel.tsx— Web 摘要审阅和 Session 开关
Gotchas
- 默认配置虽然内置
PostToolUse代码审查 sensor,但enabled=false;仅把 Hooks 打开且不覆盖PostToolUse时才会同时启用这个默认 Prompt Hook(packages/cli/src/hooks/HookConfig.ts) - Plan 模式只在
executePreToolHooks()和executePostToolHooks()中显式跳过;PermissionRequest、Session、Stop、Compaction 和 Elicitation 不能据此假设自动禁用(packages/cli/src/hooks/HookManager.ts) - Workspace Trust 与 Hook Trust 是两道独立门:外部 Hook 定义变化会使摘要变为
modified并立即停止执行,Function Hook 则不计入摘要(packages/cli/src/hooks/HookTrustService.ts) - 不带
projectDir注册的 Function Hook 是 manager 级 managed matcher,会作用于所有启用了 Hooks 的项目;带projectDir才只进入该项目配置(packages/cli/src/hooks/HookManager.ts,packages/cli/tests/unit/hooks/hook-trust-execution.test.ts) - YOLO 只把 PreToolUse 的 ask 转为 allow,不能覆盖 Hook deny;原始规则已 deny 时甚至不会运行 PreToolUse Hook(
packages/cli/src/hooks/HookManager.ts,packages/cli/src/tools/execution/ToolExecutionHooks.ts) - PreToolUse 改写参数后会重新构建 invocation、重新校验 worktree isolation 并重新计算规则权限;无效 schema 改写会作为 validation error 拒绝,而不是回退原参数(
packages/cli/src/tools/execution/ToolExecutionHooks.ts,packages/cli/src/tools/execution/ToolExecutor.ts) PermissionRequest只在合并后的决策仍为 ask 时运行,并且多个确认由 mutex 串行;它可以批准 ask 或拒绝,但不会获得改写已 deny 规则的机会(packages/cli/src/tools/execution/ToolApprovalController.ts)- HTTP
allowedHosts命中会同时绕过 HTTPS、loopback 和私网限制,不只是添加域名白名单;配置该字段等价于显式承担目标网络边界(packages/cli/src/hooks/HttpHookSecurity.ts) HttpHook.allowInsecureTLS存在于类型中,但当前 fetch 执行路径未读取该字段;不能依赖它连接自签名 HTTPS 服务(packages/cli/src/hooks/types/HookTypes.ts,packages/cli/src/hooks/HookExecutor.ts)- Function Hook 超时只能让调用方停止等待,无法强制终止已经运行的 handler;handler 必须自行监听
abortSignal并清理副作用(packages/cli/src/hooks/HookExecutor.ts) - Command Hook 的 stdin 限制 100 KiB、stdout/stderr 各 1 MiB,超时或 abort 会终止完整 owned process tree;不要在 Hook 输出中传递无界工具结果(
packages/cli/src/hooks/SecureProcessExecutor.ts,git:61a78ea9) - OutputParser 只把完整 stdout 为单个 JSON object 时视为结构化 HookOutput;JSON 前后混入日志会退回退出码语义(
packages/cli/src/hooks/OutputParser.ts) - ApplyPatch matcher 会提取 Add/Delete/Update 和 Move to 的全部路径,任一路径命中即可触发;只检查第一个 patch 文件会漏掉后续敏感目标(
packages/cli/src/hooks/HookManager.ts,packages/cli/tests/unit/hooks/function-hook.test.ts) - source project 与 Session worktree 通过 alias 共享暂停状态,但同 workspace 的其他 Session 不受影响;释放 Runtime 时必须按 Session ID 清掉全部 alias(
packages/cli/src/hooks/HookManager.ts,packages/cli/tests/unit/hooks/hook-session-scope.test.ts,git:981afccb) - Prompt Hook 的模型与环境来自 Session 冻结资源并按
session + project + model缓存;直接回退实时全局模型会破坏恢复 Session 的一致性(packages/cli/src/hooks/HookExecutor.ts,git:852c8cba)
Architecture
- HookManager 保留进程级宿主开关、LRU 有界项目配置和不受 LRU 淘汰影响的 Session 快照;活动 Session 即使项目配置缓存被逐出仍保持原行为(
packages/cli/src/hooks/HookManager.ts,git:dff75aca) - PreToolUse 串行累积
updatedInput并在首个 deny/ask 短路,PostToolUse 并行执行并合并additionalContext与最后一个有效updatedOutput(packages/cli/src/hooks/HookExecutor.ts) - Command、Prompt、Function、HTTP 四种后端统一归一到
HookExecutionResult,再由事件专用 reducer 转成权限、继续执行、内容注入、环境或压缩决策(packages/cli/src/hooks/HookExecutor.ts,packages/cli/src/hooks/OutputParser.ts) - 插件 Hooks 不逐个热插拔;Integrator 保存无插件 base config,将所有 active plugins 的 Hook 追加后一次 load,刷新时再恢复 base,避免残留已卸载插件行为(
packages/cli/src/plugins/PluginIntegrator.ts)
Decisions
- Hook Trust 使用 canonical workspace identity 与完整外部配置摘要,而非简单目录布尔值;摘要包含 matcher、策略和插件来源,但排除不可序列化的 Function Hook(
packages/cli/src/hooks/HookTrustService.ts,git:a172a7c0) - 外部 Command Hook 只继承显式 Session 环境和 PATH/HOME/USER/SHELL 最小宿主环境,避免把任意进程凭据泄漏给仓库脚本(
packages/cli/src/hooks/SecureProcessExecutor.ts,git:a172a7c0) - 项目配置缓存限制为 64 项而 Session 配置独立持有,是为了给长期 serve 进程设驻留上限,同时不改变活动会话快照(
packages/cli/src/hooks/HookManager.ts,git:dff75aca)
Patterns
- 事件输入统一携带
hook_execution_id、时间戳、project、session 和 permission mode;新增事件必须同时扩展输入联合、输出 schema、manager 构造和 executor reducer(packages/cli/src/hooks/types/HookTypes.ts,packages/cli/src/hooks/schemas/HookSchemas.ts) - Matcher 对 tools 支持精确、管道和正则,对参数支持 glob;文件工具从约定字段和 patch header 提取路径,Bash 则只从
command提取匹配值(packages/cli/src/hooks/Matcher.ts,packages/cli/src/hooks/HookManager.ts)
Branching Behavior
- 工具主链路固定为规则权限 → PreToolUse → 改写后重验 → 合并决策 → PermissionRequest/用户确认 → 执行 → PostToolUse → LSP → AutoVerify;移动 Hook 会改变权限和诊断可见性(
packages/cli/src/tools/execution/ToolExecutor.ts) - SessionStart 可阻止初始化或追加环境;其环境在 LSP 和 stdio MCP 创建前冻结并绑定给 HookExecutor,因此启动后修改配置不会追改这些子进程(
packages/cli/src/agent/runtime/SessionRuntime.ts) - Stop Hook 超时或异常按“允许停止”处理,只有明确
continue:false才让 Agent 继续一轮;外层另有 30 秒保护,不能只依赖单 Hook timeout(packages/cli/src/agent/loop/completionPolicy.ts) - Compaction Hook 明确
blockCompaction:true才抛CompactionBlockedError;Hook 自身异常 fail open,继续正常压缩或 fallback(packages/cli/src/context/CompactionService.ts) - SubagentStop 明确阻止停止时,会用
continueReason和已有 messages 再执行一次子代理,而不是把原因仅显示给用户(packages/cli/src/tools/builtin/task/task.ts) - Elicitation Hook 可以在 UI 前直接回答或拒绝,也可以在用户回答后替换/取消;无论哪条分支,最终都必须通过原 MCP requested schema(
packages/cli/src/mcp/McpClient.ts,packages/cli/src/mcp/McpElicitation.ts)