随笔

Claude Code 深度源码解读 05:Hooks 是高权限纠偏总线

拆解 UserPromptSubmit、PreToolUse、PermissionRequest、PostToolUse、Stop 等 Hook 的事件语义和安全边界。

2026-04-11 Claude Code源码解读Agent Harness技术深读

简化版入口:Hooks 纠偏

机制图
机制图

我的设问与回应

设问 1:Hooks 是插件能力,还是治理能力?

回应答案: 两者都是,但从架构上首先要当成高权限治理总线。它能在输入、工具、权限、完成路径上改变或阻断主线。

证据与证明路径: 证据来自第 5 期对 UserPromptSubmit、PreToolUse、PermissionRequest、PostToolUse、Stop / SubagentStop 的拆解。每个 target 的输出都会被翻译为不同运行时行为。

还值得继续学习或反思: 继续学习 Hook 输出如何 schema 化,以及 preview/run 分离如何帮助审计。

设问 2:Stop Hook 为什么不是所有错误的兜底?

回应答案: Stop Hook 是完成路径纠偏器。API error、工具失败、权限拒绝应该按各自路径处理,否则容易造成 error -> hook blocking -> retry -> error 的循环。

证据与证明路径: 证据来自第 4 期与第 5 期对 Stop Hook blocking feedback、stopHookActive、API error skip 的描述。

还值得继续学习或反思: 生命周期 Hook 必须绑定准确事件,不要把一个 Hook 当万能异常处理器。

源码调研主体

本组解读基于冻结源码快照进行教育、防御和架构研究。

本期主题:Hooks 如何作为事件驱动的外部纠偏层,在用户输入、工具调用、权限请求、停止完成、插件/skill/session 扩展等关键节点介入 coding agent。

1. 研究问题

本期围绕一个问题展开:

现代 coding agent 为什么需要 Hooks?Hooks 在哪些场景中真正提高稳定性、安全性和可治理性,又在哪些场景中容易被过度期待?

普通 agent 的主循环通常只包含“用户输入 -> 模型输出 -> 工具执行 -> 工具结果回灌”。这个闭环能完成任务,但缺少一个重要能力:在模型即将越过关键边界时,让外部规则、组织策略、项目约束、插件能力或临时 session 规则自动介入。Hooks 正是这个外部纠偏层。

但 Hooks 不是万能兜底。它们本质上是事件驱动的插入点:只有在被注册、被匹配、通过信任/策略检查、执行成功并返回可解析结果时,才会影响主流程。好的 Hook 机制应该做到三件事:

  1. 把纠偏点放在正确的生命周期节点,而不是等副作用已经发生后才补救。
  2. 把 Hook 输出收敛成有限、可解释、可审计的动作,例如追加上下文、阻断、请求权限、改写输入或要求继续修正。
  3. 把 Hook 自身当作高风险扩展面治理,因为 Hook 往往可以执行本地命令、访问环境变量、调用插件资源或影响权限决策。

本期先总结 Hooks 的使用场景,再切入每类 Hook 的源码实现,随后围绕 coding agent 社区通常寄予 Hooks 的贡献逐项判断实际效果,并给出可落地的实操意见。最后作为拓展阅读,对比 Claude Code 与 Codex 源码中的 Hooks 机制差异。

2. 对应总览节点

对应第 1 期总览图中的节点:

  • Hooks
  • UserPromptSubmit
  • PreToolUse
  • PermissionRequest
  • PostToolUse
  • PostToolUseFailure
  • Stop / SubagentStop
  • SessionStart / Setup / FileChanged / CwdChanged
  • Plugin / Skill / Session Hooks
  • Telemetry / Hook Events

这部分机制参考前文「Hooks 纠偏 机制图」。

图中 settings / plugin / skill / session hook config 是配置来源聚合层,不是每次推理必然存在的输入。executeHooks / hook engine 是 Hook 统一执行层;不同事件的业务语义由外层 adapter 解释。

3. Hooks 的典型使用场景

3.1 输入前纠偏:把用户请求补充成可执行任务

UserPromptSubmit 发生在用户输入被正式送入模型前。典型用途是:

  • 给模型补充项目规范、工单上下文、当前分支策略或团队约束。
  • 在用户请求触碰敏感范围时提前阻断。
  • 对 prompt 做轻量分类,例如区分普通问答、代码修改、安全审查、发布操作。

效果边界:它只能影响本次输入进入主循环的方式,不能保证后续工具调用一定安全;后续仍需要工具前 Hook 和权限系统。

3.2 工具前纠偏:把“将要发生的副作用”卡在执行前

PreToolUse 是最关键的 Hook 类型之一。模型已经产生工具调用,但工具还没有执行。典型用途是:

  • 阻止高风险 shell 命令、文件写入、网络访问或越界路径。
  • 给工具调用追加上下文,例如“这个文件属于生成物,不应编辑”。
  • 在受控条件下改写工具输入,例如把不稳定路径规范化、把命令包装为审计脚本。

效果边界:PreToolUse 比 PostToolUse 更适合防副作用,但仍不能替代权限系统。Hook allow 不应绕过 deny / ask 规则。

3.3 权限请求纠偏:把审批从 UI 扩展到策略层

PermissionRequest 发生在权限请求路径中。典型用途是:

  • 组织策略自动允许低风险操作。
  • 自动拒绝命中敏感目录、生产凭据、危险网络域名的操作。
  • headless / SDK / remote 场景下用外部控制器代替人工点击。

效果边界:它应输出明确 allow / deny 或放弃决策,不能变成“另一套隐蔽权限系统”。任何自动允许都应保留 deny 规则优先级。

3.4 工具后纠偏:把工具结果转化为下一轮修正

PostToolUsePostToolUseFailure 发生在工具返回后。典型用途是:

  • 检查工具结果是否符合项目约束,例如格式化失败、测试输出异常、写入文件不符合规则。
  • 把外部检查结果追加给模型,让下一轮修正。
  • 对 MCP 工具输出做受控更新或补充解释。

效果边界:PostToolUse 已经晚于副作用发生,所以更适合作为质量门、反馈器和审计器,不适合作为唯一安全边界。

3.5 停止前纠偏:防止“看似完成但其实没收尾”

Stop 发生在模型完成路径上。典型用途是:

  • 检查是否遗漏测试、文档、用户指定输出或安全说明。
  • 在 agent 声称完成前注入 blocking feedback,让 query loop 再跑一轮。
  • 对 subagent 完成结果做收敛检查。

效果边界:Stop Hook 只在完成路径上介入,不是所有异常路径的兜底。Claude Code 中 API error 路径会走 StopFailure 类旁路,避免把无效模型响应继续交给 Stop Hook 造成循环。

3.6 Session / Setup / File / Cwd 事件:把环境变化纳入规则

这类 Hook 不直接对应某个模型工具调用,而是对应生命周期或环境事件。典型用途是:

  • SessionStart / Setup:初始化项目约束、环境变量、session 附件。
  • CwdChanged / FileChanged:刷新 watch 路径或让系统知道工作目录、文件发生变化。
  • SubagentStart / SubagentStop:给子 agent 注入角色边界或验收结果。

效果边界:这类 Hook 更偏场景增强和治理支撑。它们提高连续性和环境适配,但不一定是每轮主推理的必经路径。

4. 关键源码入口

职责Claude Code 文件
Hook 事件类型、输出 schema、callback 类型src/types/hooks.ts
Hook 配置 schemasrc/schemas/hooks.ts
Hook 统一执行、匹配、解析、信任检查src/utils/hooks.ts
Hook 配置快照与 managed-only 策略src/utils/hooks/hooksConfigSnapshot.ts
Hook UI / 配置展示辅助src/utils/hooks/hooksSettings.tssrc/utils/hooks/hooksConfigManager.ts
PreToolUse / PostToolUse / PostToolUseFailure 工具适配层src/services/tools/toolHooks.ts
Stop / SubagentStop / teammate hook 后处理src/query/stopHooks.ts
UserPromptSubmit 调用点src/utils/processUserInput/processUserInput.ts
PermissionRequest 调用点src/utils/permissions/permissions.tssrc/hooks/toolPermission/PermissionContext.tssrc/cli/structuredIO.ts
Skill frontmatter hooks 注册src/utils/hooks/registerSkillHooks.ts
Plugin hooks 加载与热重载src/utils/plugins/loadPluginHooks.ts
Session-scoped hookssrc/utils/hooks/sessionHooks.ts
Hook 事件流输出src/utils/hooks/hookEvents.ts
职责Codex 文件
Hook crate 对外入口本地源码快照/codex-rs/hooks/src/lib.rs
Hook registry / preview / run facade本地源码快照/codex-rs/hooks/src/registry.rs
Hook engine本地源码快照/codex-rs/hooks/src/engine/mod.rs
Handler 选择与并发执行本地源码快照/codex-rs/hooks/src/engine/dispatcher.rs
Hook 输出解析本地源码快照/codex-rs/hooks/src/engine/output_parser.rs
Hook 配置发现、managed-only、trust state本地源码快照/codex-rs/hooks/src/engine/discovery.rs
PreToolUse / PermissionRequest / PostToolUse / UserPromptSubmit / Stop / Compact 事件实现本地源码快照/codex-rs/hooks/src/events/*.rs
Codex core Hook 调用适配层本地源码快照/codex-rs/core/src/hook_runtime.rs
Tool runtime 调用点本地源码快照/codex-rs/core/src/tools/registry.rs
Compact 调用点本地源码快照/codex-rs/core/src/compact.rs
MCP / shell / network permission request 调用点本地源码快照/codex-rs/core/src/mcp_tool_call.rstools/orchestrator.rstools/network_approval.rstools/runtimes/shell/unix_escalation.rs

4.1 主线与场景特殊处理分类

机制/模块分类为什么这样归类
executeHooks()主线支撑 + 安全/治理横切所有 Claude Code 用户配置、插件、skill、session Hook 的统一执行层;真正是否运行取决于事件和配置
getMatchingHooks()主线支撑负责按事件、matcher、if 条件、插件/skill 来源和 session scope 找到将运行的 Hook
captureHooksConfigSnapshot()安全/治理横切启动时捕获配置,减少隐藏修改;managed-only / disableAllHooks 策略在这里影响 Hook 来源
workspace trust check安全/治理横切Claude Code 交互模式下所有 Hook 执行前要求 workspace trust,因为 Hook 可执行任意命令
command hook安全/治理横切 + 场景增强可运行 shell/PowerShell,是最通用也最高风险的扩展方式
prompt / agent / http hook场景增强 + 安全/治理横切Claude Code 支持 LLM prompt、agent verifier 和 HTTP Hook;能力强,但引入模型/网络/外部服务不确定性
callback / function hook场景增强SDK、内部能力、session function hook 使用,通常比 shell hook 更结构化
UserPromptSubmit主线前置纠偏用户输入进入模型前的最后可编程检查点
PreToolUse主线工具治理工具执行前的关键阻断、上下文注入和输入改写点
PermissionRequest安全/治理横切位于权限请求路径,可代替或影响人工审批,但应受 deny/ask 规则约束
PostToolUse可观测/恢复支撑 + 治理横切工具成功后反馈、阻断继续或追加上下文;副作用已发生,不宜作为唯一安全边界
PostToolUseFailure可观测/恢复支撑工具失败后为模型提供修复上下文或审计附件
Stop / SubagentStop可观测/恢复支撑 + 治理横切完成路径上的验收与补救;blocking feedback 可回到 query loop
SessionStart / Setup场景增强初始化环境与上下文,受 session/source 条件控制
skill frontmatter hooks场景增强仅在 skill 被加载或调用后注册为 session-scoped hooks
plugin hooks场景增强 + 安全/治理横切插件提供的 Hook 扩展;受插件启用、热重载、managed-only 限制影响
hook progress / hook events可观测/恢复支撑用于 UI、SDK、remote 和 transcript 展示,不等于模型语义主链路
Codex PreCompact / PostCompact可观测/恢复支撑Codex 显式支持 compact 前后 Hook;Claude Code compact 相关能力更多散在 query/compact 流程和 Session/Stop 周边

5. 各类 Hooks 的具体实现

5.1 配置来源:Hook 不是单一 settings 字段

Claude Code 的 Hook 来源至少包括:

  • 用户、项目、本地、policy settings 中的 hooks
  • 插件声明的 hooks。
  • skill frontmatter 中的 hooks。
  • SDK callback hooks。
  • session hooks / function hooks。
  • 内部 callback hooks,例如 attribution、session file access。

src/utils/hooks/hooksConfigSnapshot.ts 决定配置层的基础来源:policy settings 可以 disableAllHooks,也可以 allowManagedHooksOnly;非 managed settings 只能把非 managed hooks 关掉,不能关掉 managed hooks。src/utils/hooks.tsgetHooksConfig() 再把 snapshot、registered hooks、session hooks 和 session function hooks 合并。

这个设计的贡献是把“项目规则”“组织规则”“插件能力”“本轮临时规则”都统一成 Hook 事件;代价是来源复杂,必须有清晰的优先级、source 标记和 UI/telemetry 解释。

Codex 侧也有类似分层,但实现更集中在 Rust crate。codex-rs/hooks/src/engine/discovery.rs 从 config layer stack、managed requirements、JSON/TOML hooks、plugin hook sources 中发现 handler,并根据 allow_managed_hooks_only、trust state、hook state 生成 ConfiguredHandlerHookListEntry。Codex 的文档在 本地源码快照/docs/config.md 明确:allow_managed_hooks_only 只支持在 requirements.toml 中设置。

5.2 匹配与执行:先选中,再并行运行,再按事件解释

Claude Code 的统一路径是:

event input -> hasHookForEvent -> getMatchingHooks -> executeHooks -> processHookJSONOutput -> AggregatedHookResult -> event adapter

关键细节:

  • getMatchingHooks() 根据事件构造 matchQuery。工具事件按 tool_name 匹配,SessionStart 按 source,Setup / PreCompact / PostCompact 按 trigger,FileChanged 按文件名。
  • matcher 支持精确字符串、管道分隔、正则和 *
  • 工具事件还支持 if 条件,通过 permission rule 语法对工具输入做更细匹配。
  • command / prompt / agent / http hooks 会去重;plugin / skill hook 的去重 key 带 source context,避免不同插件同名脚本互相抵消。
  • Hook 执行前有 workspace trust 检查,交互模式下未信任 workspace 会跳过所有 Hook。
  • Hook 批次并行执行,并产生 progress message、attachment、telemetry。

Codex 的对应路径是:

request -> preview_* -> HookStartedEvent -> run_* -> execute_handlers -> parse_completed -> HookCompletedEvent -> core adapter

codex-rs/hooks/src/engine/dispatcher.rsselect_handlers_for_matcher_inputs() 选择 handler,按事件名和 matcher 匹配。Codex 对 handler 不做“相同命令去重”:测试明确保留重复 Stop handlers 和重叠 SessionStart matchers。执行上使用 FuturesUnordered 并发运行,结果按配置顺序返回,同时记录 completion order。PreToolUse 的多个 input rewrite 会选择“实际完成最晚”的 updated input。

5.3 UserPromptSubmit:输入前的规则门

Claude Code 调用点在 src/utils/processUserInput/processUserInput.tsprocessUserInputBase() 先把用户输入解析成消息、命令、附件等,然后执行 executeUserPromptSubmitHooks()。Hook 可返回:

  • blocking error:阻断本次 prompt。
  • additionalContext:作为 Hook 附加上下文进入当前任务。
  • preventContinuation / stopReason:停止继续处理。

这类 Hook 的位置非常早,适合补充任务背景和阻断明显越界请求,但不适合承担工具级安全。因为用户输入还没有展开成具体工具调用,Hook 对后续行动只能做粗粒度判断。

Codex 的实现位于 codex-rs/hooks/src/events/user_prompt_submit.rs。它构造 UserPromptSubmitCommandInput,把 promptmodelpermission_modecwdtranscript_path、subagent 信息传给命令 Hook。输出解析支持:

  • additional_context
  • continue:falsedecision:block 触发停止。
  • exit code 2 + stderr 作为阻断原因。
  • 非 JSON stdout 在某些情况下作为 additional context。

5.4 PreToolUse:副作用前的关键卡点

Claude Code 中 src/services/tools/toolHooks.tsrunPreToolUseHooks() 调用 executePreToolHooks()。它会把 Hook 结果转为工具执行链可理解的事件:

  • hookPermissionResult:allow / ask / deny。
  • hookUpdatedInput:改写工具输入。
  • additionalContext:追加上下文。
  • preventContinuation / stopReason:停止继续。
  • stop:Hook 被 abort 后停止工具执行。

随后 resolveHookPermissionDecision() 负责把 Hook 权限结果和常规权限系统合并。关键不变量是:Hook allow 不绕过 deny / ask 规则。若工具要求用户交互、上下文要求 canUseTool,或者规则层返回 ask / deny,仍会进入常规权限决策。

Codex 的实现位于 codex-rs/hooks/src/events/pre_tool_use.rs。PreToolUse request 包含 canonical tool_name、matcher aliases、tool_use_id 和 JSON tool_input。输出解析支持:

  • block reason:阻断工具执行。
  • additional context:记录并注入 turn context。
  • updated input:如果没有 block,允许改写工具输入。

Codex core 在 本地源码快照/codex-rs/core/src/tools/registry.rs 的工具执行路径中调用 run_pre_tool_use_hooks();adapter 会把 blocked 结果转成模型可见的工具错误,例如 Bash/apply_patch 会包含命令上下文,其他工具包含工具名。

5.5 PermissionRequest:审批路径中的策略代理

Claude Code 的 PermissionRequest Hook 入口分布在常规权限系统、React permission context 和 SDK structured IO 中:

  • src/utils/permissions/permissions.ts
  • src/hooks/toolPermission/PermissionContext.ts
  • src/cli/structuredIO.ts

src/types/hooks.tsPermissionRequest 的 hookSpecificOutput 支持:

  • behavior: allow,可带 updatedInputupdatedPermissions
  • behavior: deny,可带 message 和 interrupt。

这类 Hook 的作用是把审批决策从 UI 扩展到可编程策略。但它不能变成黑箱自动通行,否则会削弱权限系统的可解释性。

Codex 的实现位于 codex-rs/hooks/src/events/permission_request.rs。源码注释明确它运行在 approval path 中,早于 guardian 或用户审批 UI。它不改写工具输入、不直接 block 操作,而是返回 concrete allow / deny 或不决策。决策折叠规则很保守:任意 deny 优先;否则最后一个 allow 生效;否则返回 None 让正常审批继续。

Codex core 在 MCP、shell escalation、tool orchestrator、network approval 等路径调用 run_permission_request_hooks(),说明它的 PermissionRequest Hook 是一个横跨多类风险动作的策略层。

5.6 PostToolUse / PostToolUseFailure:结果后反馈

Claude Code 的 runPostToolUseHooks()runPostToolUseFailureHooks() 位于 src/services/tools/toolHooks.ts。PostToolUse 可以:

  • 产出 progress / attachment message。
  • 记录 blocking error。
  • preventContinuation,阻止继续。
  • 追加 additional context。
  • 对 MCP 工具输出返回 updatedMCPToolOutput

PostToolUseFailure 负责工具失败后的 hook 反馈,帮助模型理解失败原因或追加外部诊断。

Codex 的 PostToolUsecodex-rs/hooks/src/events/post_tool_use.rs。它接收稳定化后的 tool_inputtool_response,而不是原始内部 tool data。输出支持:

  • additional context。
  • block / feedback message。
  • continue:false 触发 stopped 状态。

Codex 当前 Hook 事件列表中没有独立 PostToolUseFailure,这和 Claude Code 是一个明显差异:Claude Code 对成功和失败后处理分得更细,Codex 的公开事件面更小。

5.7 Stop / SubagentStop:完成路径上的验收器

Claude Code 的 Stop Hook 适配在 src/query/stopHooks.ts。它只在 query loop 的完成路径上运行,并且有几个重要边界:

  • API error 完成时不交给 Stop Hook,而是使用 StopFailure 类逻辑。
  • blocking error 会被转成 meta user message,回到 query loop 让模型补救。
  • preventContinuation 会生成 hook_stopped_continuation attachment。
  • subagent / teammate / task completed 等场景有额外分支。

这使 Stop Hook 更像“验收和返工机制”,而不是“异常兜底机制”。

Codex 的 Stop 实现位于 codex-rs/hooks/src/events/stop.rs。它支持 StopSubagentStop 两个 target。输出可产生:

  • should_stop / stop_reason
  • should_block / block_reason
  • continuation_fragments,用于给后续 turn 注入继续修正的 prompt fragment。

Codex core 的 run_turn_stop_hooks()core/src/hook_runtime.rs 中根据 session source 判断是 root Stop 还是 thread-spawned child 的 SubagentStop,并发送 hook started/completed events。

5.8 Compact Hooks:Codex 更显式,Claude Code 更内嵌

Codex 明确支持 PreCompactPostCompact。事件实现位于 codex-rs/hooks/src/events/compact.rs,核心 compact 路径在 core/src/compact.rscompact_remote.rscompact_remote_v2.rs 中调用:

  • PreCompact 可阻止 compact 继续。
  • PostCompact 在 compact 后运行,属于状态/审计类 Hook。
  • matcher 按 trigger,例如 manual / auto。

Claude Code 的路线图第 4 期已覆盖 compact 机制。本地源码中 Hook 事件类型也包含 PreCompact / PostCompact,但第 5 期关注的代表链路是 UserPromptSubmit / PreToolUse / PermissionRequest / Stop。从现有调研看,Claude Code compact 纠偏更多嵌入 query loop、autoCompact、Stop hooks、session/transcript 支撑中,而 Codex 将 compact 前后 Hook 作为一等事件对外暴露得更清晰。

6. Hooks 被寄予的贡献:效果判断与实操意见

6.1 贡献一:让 agent 遵守项目和组织规则

效果判断:有效,但只对可被事件化、可被检测的规则有效。

Hooks 最适合表达“在某个生命周期点检查某个条件”。例如:写入前检查路径、执行命令前检查命令模式、完成前检查测试记录。它不适合表达复杂、长期、语义模糊的工程判断,例如“代码架构是否优雅”。

实操意见:

  • 把规则拆到最早能判断的 Hook:能在 PreToolUse 判断,就不要拖到 Stop。
  • 对高风险动作使用 deny / ask,不要只追加 warning context。
  • 对软性规范使用 additionalContext,让模型修正,而不是硬阻断。

6.2 贡献二:减少模型幻觉带来的真实副作用

效果判断:PreToolUse + PermissionRequest 能显著降低风险;PostToolUse 只能事后补救。

模型幻觉工具名、参数、路径或命令时,主工具链的 schema 校验和权限系统已经能拦一部分。Hook 的价值在于加入项目本地语义,例如哪些路径是生成物、哪些命令在本仓库危险、哪些外部服务不能触碰。

实操意见:

  • 对 shell 命令建立 allowlist / denylist,并在 Hook 输出中给出简短、可修正的原因。
  • 对文件写入检查路径类别,例如源码、配置、生成物、密钥、锁文件。
  • 不要用 PostToolUse 作为唯一防线,因为文件或命令副作用已经发生。

6.3 贡献三:把隐性规范变成可审计记录

效果判断:有效,前提是 Hook 事件、输出、来源和状态进入 transcript / telemetry。

Claude Code 会产生 progress、attachment、hook event;Codex 会产生 HookStartedEvent / HookCompletedEvent 和 HookRunSummary。两者都说明现代 agent 不只需要执行 Hook,还需要展示 Hook 是否运行、来自哪里、是否阻断、耗时多久、输出了什么。

实操意见:

  • Hook 输出必须短、结构化、可读,避免把大段日志塞进模型上下文。
  • 为每条 Hook 规则保留 source、matcher、状态和简短 reason。
  • 对企业/团队策略优先使用 managed hooks,并限制用户/项目 Hook 覆盖能力。

6.4 贡献四:让 agent 自动补救,而不是直接失败

效果判断:Stop Hook 和 PostToolUse 在这方面有效,但容易引入循环。

Claude Code 的 Stop Hook blocking feedback 会回到 query loop;Codex 的 StopOutcome 有 continuation fragments。这些都让 agent 能在“将要结束”时被拉回去修正。

实操意见:

  • Stop Hook 只检查可验证的完成条件,例如“测试命令是否出现”“是否修改了指定文档”“是否提供了风险说明”。
  • 每个 Stop Hook 要有防循环条件,例如只阻断一次、检查已有修正痕迹、或通过 stop_hook_active 避免重复。
  • 反馈要直接说明缺口,不要输出泛化建议。

6.5 贡献五:支持插件、skill 和团队级扩展

效果判断:有效,但这是扩展能力,同时也是供应链风险。

Claude Code 和 Codex 都支持 plugin hooks。Claude Code 还支持 skill frontmatter hooks、session hooks、callback hooks、prompt/agent/http hooks。能力越强,越需要清晰 trust boundary。

实操意见:

  • 插件 Hook 默认只做低风险检查;高风险 Hook 应来自 managed / policy source。
  • 插件 Hook 要显示来源,不要隐藏在普通项目配置里。
  • 对 Hook 可访问的环境变量和路径做最小化,尤其是 command/http hooks。

6.6 贡献六:替代人工审批

效果判断:只能部分替代。

PermissionRequest Hook 可以自动允许或拒绝,但它不应该替代所有用户判断。尤其是改写文件、执行 shell、访问网络、使用凭据、跨仓库操作时,自动允许需要非常明确的条件。

实操意见:

  • deny 比 allow 更适合自动化;自动拒绝高风险命中,自动允许低风险只限窄范围。
  • 让 Hook allow 继续受 deny / ask 规则约束。
  • 对 headless 场景保留审计日志,避免“自动批准但无人知道”。

6.7 贡献七:提高长任务稳定性

效果判断:间接有效。

Hooks 不直接扩大上下文窗口,也不直接提升模型推理能力。它们提高稳定性的方式是:在关键转折点把外部状态、错误反馈、验收条件重新注入循环。

实操意见:

  • 长任务中的 Hook 要少而准,避免每个工具调用都运行昂贵检查。
  • 对耗时 Hook 使用 timeout,并把失败设计成 non-blocking 或明确阻断,不要卡死 turn。
  • 对输出量大的 Hook 做摘要或 spill,不要污染模型上下文。

7. 风险、边界与待验证问题

7.1 Hook 本身是高权限执行面

Claude Code 的源码注释明确:所有 Hooks 都需要 workspace trust,因为 .claude/settings.json 中的 Hook 可执行任意命令。Codex 侧也有 trust state、managed hooks、hook state 和 allow_managed_hooks_only。这说明 Hook 机制的安全边界不是“模型能不能调用工具”,还包括“配置能不能注册可执行程序”。

实操建议:

  • 未信任 workspace 不运行 project/local hooks。
  • enterprise / team 策略使用 managed hooks。
  • 用户 Hook、项目 Hook、插件 Hook、session Hook 在 UI 和审计中必须区分来源。

7.2 Hook 输出会影响模型上下文,存在 prompt injection 面

additionalContext、feedback、continuation fragments 最终会变成模型可见内容。若 Hook 读取不可信文件或外部服务输出,等于把外部文本注入 agent 主循环。

实操建议:

  • Hook 输出应由可信脚本生成,避免直接拼接未清洗的第三方内容。
  • 对模型可见的 Hook 输出使用固定模板,例如“检查项、结果、建议修正”。
  • 安全策略型 Hook 尽量用 block/deny,而不是把复杂安全解释交给模型自行判断。

7.3 Hook 失败策略必须按事件区分

同样是 Hook 失败,PreToolUse、PermissionRequest、PostToolUse、Stop 的处理不应相同。工具前失败若被当作通过,可能放大风险;Stop Hook 失败若强行阻断,可能导致任务无法结束。

实操建议:

  • 高风险工具前 Hook:失败默认 deny 或 ask。
  • 质量检查型 Post/Stop Hook:失败默认 non-blocking,但提示用户。
  • 企业强制策略:失败默认阻断,并明确恢复方式。

7.4 Hook 过多会拖慢 agent

Hook 是每个事件点的额外执行成本。Claude Code 有 fast path 跳过没有 Hook 的事件,也对内部 callback hooks 做优化;Codex 有 preview/run 和并发执行,但仍然要承担进程启动、JSON 序列化、输出解析成本。

实操建议:

  • 优先使用 matcher 和 if 条件缩小触发范围。
  • 把昂贵检查放在 Stop 或 PostToolUse,不要放在所有 PreToolUse。
  • 将多个轻量规则合并为一个脚本,避免进程启动风暴。

7.5 待验证问题

  • Claude Code 的 PreCompact / PostCompact 在本地快照中的实际调用点需要单独做一次 compact Hook 专题核验。
  • Claude Code 的 prompt / agent / http hooks 对性能和外部依赖的影响,需要结合运行日志验证。
  • Codex 的 Hook feature flag、trust state、managed hooks 在不同产品形态下的默认开启状态,需要结合配置加载路径进一步确认。

8. 拓展阅读:Claude Code 与 Codex Hooks 机制差异

8.1 总体架构差异

维度Claude CodeCodex
实现形态TypeScript 中央执行器 src/utils/hooks.ts + 多个业务 adapter独立 Rust crate codex-rs/hooks + core hook_runtime.rs adapter
事件体系事件更多,包含 PostToolUseFailure、PermissionDenied、Notification、Setup、ConfigChange、FileChanged、WorktreeCreate 等HOOK_EVENT_NAMES 当前 10 个:PreToolUse、PermissionRequest、PostToolUse、PreCompact、PostCompact、SessionStart、UserPromptSubmit、SubagentStart、SubagentStop、Stop
Hook 类型command、prompt、agent、http、callback、function当前 lifecycle hooks 以 command handler 为主;另有 legacy notify after-agent
配置来源settings、policy、plugins、skills、SDK callbacks、session hooks、内部 callbacksconfig layer stack、managed requirements、hooks.json/TOML、plugin hook sources、legacy notify
执行模型统一 executeHooks() 并行执行,按事件 adapter 聚合preview/run 两阶段,HookStartedEvent / HookCompletedEvent 明确建模
权限关系Hook allow 不绕过 deny / ask;权限 UI、SDK、bridge 场景深耦合PermissionRequest Hook 返回 allow/deny/None;deny wins,normal approval flow 继续
Compact Hook类型存在,但本期未确认完整调用面PreCompact / PostCompact 是一等事件,并在 compact 路径显式调用
可观测性progress message、attachments、hook events、analytics、OTelHookRunSummary、HookStartedEvent、HookCompletedEvent、analytics facts

8.2 Claude Code 更像“可编程扩展总线”

Claude Code Hooks 覆盖更宽,和插件、skill、SDK、session、UI 权限、prompt hook、agent hook、HTTP hook 都有连接。这使它更适合构建丰富的本地自动化和产品内扩展,例如:

  • skill 启用后临时注册 session hooks。
  • 插件提供 Hook、MCP、commands 等组合能力。
  • SDK callback hooks 接入宿主应用。
  • prompt / agent hooks 做 LLM verifier。

代价是治理面更复杂。Hook 来源、执行类型、权限模式、workspace trust、managed-only、plugin hot reload、session scope 都必须正确协同,否则容易出现“Hook 为什么没跑”“谁阻断了工具”“哪个插件改写了输入”等可解释性问题。

8.3 Codex 更像“生命周期策略引擎”

Codex 把 Hooks 抽成 codex-hooks crate,事件输入/输出 schema 更集中,preview/run 分离更清晰。它的强项是:

  • 事件前可以 preview,UI/协议能显示即将运行的 Hook。
  • Hook completed event 结构稳定,便于 app server、SDK、analytics 消费。
  • Compact hooks 是一等事件,适合围绕 context 生命周期做治理。
  • discovery 层把 managed requirements、plugin hooks、trust state 和 hook state 集中处理。

代价是当前事件面和 Hook 类型相对收敛。与 Claude Code 相比,Codex 当前源码中没有独立 PostToolUseFailure 事件,也没有等价的 prompt/agent/http hook 类型作为本地 Hook handler 主线。

8.4 对 coding agent 设计的启示

如果目标是产品级 agent,可采用 Codex 的 preview/run 和 HookRunSummary 结构,让 UI、SDK、审计系统先看到“哪些 Hook 会跑”。如果目标是高度可扩展的本地 agent,可采用 Claude Code 的多来源、多类型 Hook 总线,但必须强制治理:

  • Hook source 必须进入审计。
  • Hook allow 不能绕过 deny / ask。
  • 未信任 workspace 不运行可执行 Hook。
  • additionalContext 必须可控、短小、可解释。
  • Stop Hook 必须防循环。
  • Plugin / skill Hook 必须有隔离和可见性。

9. 下一期衔接

第 6 期将进入 Memory 连续性机制。Hooks 与 Memory 的边界需要特别区分:

  • Hooks 是事件驱动纠偏:在某个生命周期点运行外部规则。
  • Memory 是跨回合连续性:把长期经验、项目偏好、session 记录带回当前上下文。

二者会交叉,例如 Stop Hook 可以触发记忆提取,UserPromptSubmit Hook 可以追加与 memory 相关的上下文。但写作上必须避免把一次性 Hook 附件误写成长期 Memory,也不能把异步 memory prefetch 误写成每轮动态反馈。

机制主线收束

站在架构师视角,我会把这组源码机制收束为三个问题:为什么需要它,什么时候必须引入它,以及真正要设计的是什么。

  • why:Hooks 解决的是外部规则如何在关键生命周期点进入 agent,而不是事后靠人审日志。
  • when:项目规范、安全规则、组织策略、完成验收需要自动介入时使用 Hook。
  • what:为每个 Hook target 定义输入输出 schema、失败策略、source、matcher、duration、decision,并把高权限 Hook 放进 trust boundary。

Hooks 纠偏 这组源码最值得带走的,不是某个函数或某个配置项,而是它如何把模型能力放进一组可验证、可拒绝、可恢复、可审计的工程边界里。读源码时,如果只记住名词,会很快散;如果抓住 why、when、what,就能把这组机制迁移到自己的 agent 架构判断中。