diff --git a/docs/en/customization/hooks.md b/docs/en/customization/hooks.md index 1f966e341..14e282746 100644 --- a/docs/en/customization/hooks.md +++ b/docs/en/customization/hooks.md @@ -98,7 +98,7 @@ Only **blockable events** (`PreToolUse`, `Stop`, `UserPromptSubmit`) have return | Event | Matcher matches | Supports blocking? | Description | | --- | --- | --- | --- | -| `UserPromptSubmit` | The text submitted by the user | ✓ | Triggered when the user sends a message; returned text is appended to context; if blocked, the model is not called for this turn | +| `UserPromptSubmit` | The text submitted by the user | ✓ | Triggered when the user sends a message; the payload includes a `prompt` field of type `ContentPart[]` (see note below); returned text is appended to context; if blocked, the model is not called for this turn | | `PreToolUse` | Tool name | ✓ | Triggered before a tool call (before permission checks); the tool will not execute if blocked | | `Stop` | Empty string | ✓ | Triggered when the model is about to end the current turn; if blocked, a message can be appended to let the model continue | | `PostToolUse` | Tool name | — | Triggered after a tool executes successfully (observation only) | @@ -115,6 +115,30 @@ Only **blockable events** (`PreToolUse`, `Stop`, `UserPromptSubmit`) have return | `PostCompact` | `manual` or `auto` | — | Triggered after context compaction completes (observation only) | | `Notification` | Notification type (e.g. `task.completed`) | — | Triggered when a background task status changes (observation only) | +::: tip `UserPromptSubmit` payload: `prompt` is a `ContentPart[]`, not a string +The `UserPromptSubmit` payload's `prompt` field is the raw `ContentPart[]` array, **not** a plain string: + +```json +{ + "hook_event_name": "UserPromptSubmit", + "session_id": "session_abc", + "cwd": "/path/to/project", + "prompt": [{ "type": "text", "text": "user's message" }] +} +``` + +The `matcher` regex is applied to the **joined text** of those parts (all `text` parts joined with a space), which is why the Event Reference says "The text submitted by the user" — that describes the matcher subject, not the payload field. + +To recover the plain text string in your hook: + +```js +const text = payload.prompt + .filter((p) => p.type === 'text') + .map((p) => p.text) + .join(' '); +``` +::: + ## Example: Blocking Dangerous Shell Commands The following hook checks the command content before the Agent calls the `Bash` tool and blocks it if `rm -rf` is detected: diff --git a/docs/zh/customization/hooks.md b/docs/zh/customization/hooks.md index a506923b9..cd389cc09 100644 --- a/docs/zh/customization/hooks.md +++ b/docs/zh/customization/hooks.md @@ -98,7 +98,7 @@ Hook 命令的工作目录是当前会话的项目目录。非 Windows 平台上 | 事件 | Matcher 匹配的是 | 会触发阻断? | 说明 | | --- | --- | --- | --- | -| `UserPromptSubmit` | 用户提交的文本内容 | ✓ | 用户发送消息时触发;返回文本会附加到上下文;若阻断,本轮不调用模型 | +| `UserPromptSubmit` | 用户提交的文本内容 | ✓ | 用户发送消息时触发;payload 包含 `prompt` 字段,类型为 `ContentPart[]`(见下方说明);返回文本会附加到上下文;若阻断,本轮不调用模型 | | `PreToolUse` | 工具名 | ✓ | 工具调用前触发(权限检查前);阻断后工具不会执行 | | `Stop` | 空字符串 | ✓ | 模型准备结束本轮时触发;阻断后可追加一条消息让模型继续 | | `PostToolUse` | 工具名 | — | 工具成功执行后触发(观察用) | @@ -115,6 +115,30 @@ Hook 命令的工作目录是当前会话的项目目录。非 Windows 平台上 | `PostCompact` | `manual` 或 `auto` | — | 上下文压缩完成后触发(观察用) | | `Notification` | 通知类型(如 `task.completed`) | — | 后台任务状态变化时触发(观察用) | +::: tip `UserPromptSubmit` 的 payload:`prompt` 是 `ContentPart[]`,不是字符串 +`UserPromptSubmit` 的 payload 中 `prompt` 字段是原始的 `ContentPart[]` 数组,**不是**普通字符串: + +```json +{ + "hook_event_name": "UserPromptSubmit", + "session_id": "session_abc", + "cwd": "/path/to/project", + "prompt": [{ "type": "text", "text": "用户的消息" }] +} +``` + +`matcher` 正则匹配的是这些内容部分的**拼接文本**(所有 `text` 部分用空格连接),所以事件一览表中写的是"用户提交的文本内容"——这描述的是 matcher 匹配的对象,而非 payload 字段。 + +在 hook 中提取纯文本字符串: + +```js +const text = payload.prompt + .filter((p) => p.type === 'text') + .map((p) => p.text) + .join(' '); +``` +::: + ## 示例:阻断危险 Shell 命令 下面的 hook 在 Agent 调用 `Bash` 工具前检查命令内容,发现 `rm -rf` 就阻断: