功能指南 / Hook 自动化与拦截

Hook 自动化与拦截

Hook 在内核执行流程的指定时点运行检查或通知。配置文件位于项目的 .lxz/hooks/*.json,也可通过 IDE 设置中的自动化/Hook 页面管理。Hook 与 MCP 工具和 A2A 任务接口有不同职责。

1. 创建第一个 Command Hook

前置条件:本机有 Node.js,node 位于内核进程的 PATH 中;当前项目是你信任的目录。项目 Hook 能以当前用户身份启动进程,应先检查脚本内容。

在项目创建 .lxz/hooks/check-tool.json,完整内容如下。除了默认可省略的 handler,其他字段均需要提供。

{
  "name": "check-tool",
  "event": "BeforeToolCall",
  "command": "node",
  "args": [".lxz/scripts/check-tool.cjs"],
  "enabled": true,
  "timeout_secs": 5,
  "handler": "Command"
}

创建 .lxz/scripts/check-tool.cjs:

// 输入来自环境变量;stdin 已关闭,不要等待用户输入。
const raw = process.env.LXZ_HOOK_DATA || ''
// 演示规则:匹配到此标记时拦截。实际规则应解析对应工具的数据。
const blocked = raw.includes('LXZ_DEMO_BLOCK')
process.stdout.write(JSON.stringify(blocked
  ? { action: 'block', reason: '演示规则阻止了本次工具调用' }
  : { action: 'continue' }))

Command 的工作目录为项目根目录,args 是独立参数数组,不会自动通过 shell 展开。Windows 上运行 .ps1 或内置命令时应显式指定对应解释器。脚本诊断写到 stderr,stdout 只返回一个决策 JSON。

可在项目目录先独立检查脚本:

$env:LXZ_HOOK_DATA = '{"demo":"ok"}'
node .lxz/scripts/check-tool.cjs
$env:LXZ_HOOK_DATA = '{"demo":"LXZ_DEMO_BLOCK"}'
node .lxz/scripts/check-tool.cjs
Remove-Item Env:LXZ_HOOK_DATA

预期分别为 {"action":"continue"} 和含非空 reason 的 block。独立运行验证脚本输入输出,实际挂接还需在 IDE 发起一次工具调用。

2. 输入、决策与失败

Command 的项目上下文由工作目录体现;调用点还可以显式提供环境变量。LXZ_HOOK_DATA 是当前调用点传入的原始字符串,不保证所有事件都有相同 JSON 字段,也不保证包含生命周期标识。按事件校验数据,不把它直接拼接进命令或当成可信指令。

异步 Command/Prompt/Mcp 决策只接受严格 JSON:

{"action":"continue"}
{"action":"block","reason":"请先完成必要检查"}

不附带 Markdown、额外字段或普通文本,block.reason 必须非空。运行时 Command 非零退出、超时、输出超限或非法决策会产生失败并拦截。stdout、stderr 各限制 1 MiB;超时至少按 1 秒处理,取消或超时会终止子进程树。需要长时间交互的命令不适合作为 Hook。

BeforeToolCall 可以阻止随后操作;AfterToolCall 运行时原操作已经发生,后置拦截不能撤销其副作用。旧同步 fire 接口中的 BLOCK: 文本约定不适用于这里的 IDE 异步运行时。

3. 当前接入的事件

调用链已接入事件
工具执行BeforeToolCall、AfterToolCall
Agent/RUSHBeforeAgentRun、AfterAgentRun;开始事件也存在 fallback 路径
RUSH 五阶段BeforeGuide/AfterGuide、BeforePlan/AfterPlan、BeforeCode/AfterCode、BeforeVerify/AfterVerify、BeforeLearn/AfterLearn

配置类型还包含文件写入、命令执行、提交、会话、错误等事件名称,但枚举存在不代表每个操作都已触发它。不要依赖 BeforeFileWrite、BeforeCommit 等名称实现当前版本的强制安全策略;先在实际运行链观察事件。

4. 其他处理器

保留完整外层字段,将 handler 替换为下列对应对象。非命令处理器的 command 可填空字符串,args 填空数组。

处理器handler 示例运行条件
HTTP{"Http":{"url":"http://127.0.0.1:8787/hook","timeout_secs":3}}HTTP 服务可访问
MCP{"Mcp":{"server":"checks","tool":"validate","arguments":"{}"}}对应 MCP 服务和工具已配置,运行时 executor 可用,遵守工具批准
Prompt{"Prompt":{"prompt":"检查本次调用并仅返回决策 JSON"}}模型和运行时 executor 可用

Mcp.arguments 是 JSON 字符串,不是对象。处理器名称区分大小写且使用外部标签,例如 Http 而非 type: http。类型定义还包含 Agent,但当前配置加载和 IDE 保存路径拒绝启用 Agent Hook;不要将它当作可用功能。PluginCommand 由插件加载与指纹批准流程生成,不应手工伪造批准字段。

HTTP 使用 POST,载荷字段为 event、tool_name、args、session_id、run_id、event_id、sequence、occurred_at;缺失字段可为 null,args 仍是字符串。服务正常返回 2xx 和 {"action":"continue"} 或 {"action":"block","reason":"原因"}。HTTP 路径在网络错误、超时、非 2xx、无效响应或未知动作时记录警告并继续,即 fail-open,不能作为强制安全边界;它的响应解析也不同于严格 Command 决策。

5. 保存、开关与重新加载

使用实际监听地址和本地 Bearer token;获取方式见 内核 API。不要把 token 写进配置文件或日志。

# $kernelBase、$kernelToken 使用当前内核实际值。
$hookHeaders = @{ Authorization = "Bearer $kernelToken" }
$projectRoot = (Get-Location).Path
$rootQuery = [uri]::EscapeDataString($projectRoot)
Invoke-RestMethod "$kernelBase/api/hooks/list?project_root=$rootQuery" -Headers $hookHeaders
$toggle = @{ name = 'check-tool'; enabled = $true; project_root = $projectRoot } | ConvertTo-Json
Invoke-RestMethod "$kernelBase/api/hooks/toggle" -Method Post -Headers $hookHeaders -ContentType 'application/json' -Body $toggle
Invoke-RestMethod "$kernelBase/api/hooks/reload?project_root=$rootQuery" -Method Post -Headers $hookHeaders

保存接口为 POST /api/hooks/save,请求为 { "hook": <完整配置对象>, "file": "check-tool.json", "project_root": "项目绝对路径" }。开关和保存会写入项目文件。reload 返回加载结果,不能保证替换已经在运行的引擎实例;改动后启动新任务验证。部分错误通过响应中的 success: false 表达,不能仅检查 HTTP 200。

6. 排错

现象检查
Hook 没运行实际项目根目录、.lxz/hooks、enabled、事件是否接入、新任务是否读取配置
配置失败并拦截JSON 必填字段、handler 标签、Agent 限制;修复文件后重新加载
进程无法启动内核 PATH、Node 安装、相对脚本路径、解释器及文件权限
一直超时是否误读 stdin、等待交互、网络请求缺少超时
非法决策stdout 是否有日志、是否输出严格 JSON、reason 是否非空
HTTP 故障仍继续当前 HTTP 是 fail-open;检查服务日志和 hook 警告
后置 Hook 拦截后文件仍改变后置时原操作已完成,使用已接入的前置事件做检查

A2A 指南 · MCP 配置 · 内核 API

v1.0.0 · web-2026