---
title: "如何知道 Claude Code、Codex 或 OpenCode 正在等你"
description: "Claude Code、Codex、OpenCode 和 Hermes 的哪些钩子表示智能体在等你，哪些应该忽略，以及怎样写出给智能体开销很小的钩子。"
author: "Foxl Team"
date_published: "2026-10-23"
date_modified: "2026-10-23"
canonical_url: "https://foxl.ai/zh/blog/coding-agent-waiting-for-input"
markdown_url: "https://foxl.ai/zh/blog/coding-agent-waiting-for-input/index.md"
image: "https://foxl.ai/blog/coding-agent-waiting-for-input-cover.png"
social_image: "https://foxl.ai/blog/social/coding-agent-waiting-for-input.png"
content_type: "Deep dive"
topics: ["Claude Code","Codex","Hooks","Coding agents","Notifications"]
products: ["Foxl Code"]
---

<!-- Generated by scripts/blog-publishing.mjs; do not edit. -->

# 如何知道 Claude Code、Codex 或 OpenCode 正在等你

> Claude Code 通过三个钩子告诉你它在等你：AskUserQuestion 上的 PreToolUse、permission\_prompt 类型的 Notification，以及最后一条消息以问题结尾的 Stop。请忽略 idle\_prompt，它会跟在任何一条没人回应的回复之后到来。本文介绍 Foxl Code 为远程 Claude Code、Codex、OpenCode 和 Hermes 终端使用的对应关系、每个 CLI 无法上报的内容，以及怎样写出给智能体开销很小、又绝不会意外把内容送进模型的钩子。

- Author: Foxl Team
- Published: 2026-10-23
- Last reviewed: 2026-10-23
- Reading time: 11 minutes
- Canonical HTML: [https://foxl.ai/zh/blog/coding-agent-waiting-for-input](<https://foxl.ai/zh/blog/coding-agent-waiting-for-input>)

![一个手绘的终端窗口，光标停在一个问号下方；从窗口里伸出的一条线末端是一个钩子，钩住了一部显示着铃铛的手机。](<https://foxl.ai/blog/coding-agent-waiting-for-input-cover.png>)

Claude Code 的钩子（hooks）会在特定事件发生时运行你配置好的命令，其中有三个能告诉你它正在等你。`AskUserQuestion` 工具上的 `PreToolUse` 钩子，会在它打开提问对话框时触发。类型为 `permission_prompt` 的 `Notification` 钩子，会在权限对话框打开几秒后触发。`Stop` 钩子在每次回复结束时触发，并在 `last_assistant_message` 中带上回复的文本，所以你可以检查回复是不是以问题结尾。请忽略 `idle_prompt`：只要一条回复之后没人输入，大约一分钟后它就会到来，并不只在提问之后。

Foxl Code 在远程终端中运行编程智能体，它正是用这三个钩子，把远程 Claude Code 终端标记为“在等用户”。从 v0.9.3-beta.5 起，同一个上报命令也会通过 Codex、OpenCode 和 Hermes 各自的钩子和插件读取它们的状态。下面依次介绍每个 CLI 的对应关系、各自的陷阱，以及怎样写钩子，才能让它给智能体带来的开销尽可能小，并且永远不会把文本漏进模型的上下文。目前还没有任何线上云端终端上报过提示；最后一节说明了哪些还没有验证。

## 为什么你看不出终端里的智能体在等你？

在远程终端里运行智能体的人，可能同时开着好几个，却一个都没在看。2026-10-21，一个远程 Claude Code 终端问了一个是非题，停在提示符那里，而 Foxl 自己的 API 却说没有任何任务在等人。Foxl Code 的侧边栏、列出等人处理的任务的“待你处理”列表，以及手机应用，全都没有动静。在这次改动之前，Foxl Code 里没有任何东西在监听这一刻。

读屏幕看起来像是解决办法，但很容易出问题。对外部读取者来说，思考中、已完成和正在提问这几种画面，只差 CLI 自己绘制的几个字符，而任何一个版本都可能把它们重新画过。有一次我们升级了随附的 CLI 版本，新版本画出的第一屏和我们的输入检查所等待的不一样，结果远程 Codex 和 OpenCode 终端在十天里悄无声息地忽略了所有输入。钩子是 CLI 自己对刚刚发生了什么的声明，数据格式有文档可查，所以我们监听的是钩子。

## 哪些 Claude Code 钩子表示“我在等你”？

我们的云端镜像固定使用 Claude Code 2.1.296。下文中标注为“从包中读取”的细节，来自该版本的 npm 包，而不是来自一个正在运行的终端。

| 钩子 | 含义 | 我们视为 |
| --- | --- | --- |
| `PreToolUse`，matcher 为 `AskUserQuestion` | 提问对话框即将打开；`tool_input` 中带有问题及其选项 | 一个问题，附带选项 |
| `Notification`，matcher 为 `permission_prompt` | 权限对话框已打开约六秒，期间没有任何按键（从包中读取） | 一个权限请求 |
| `Stop` | 回复已结束；`last_assistant_message` 是它的文本 | 文本以 `?` 结尾则视为问题，否则视为一轮结束 |
| `UserPromptSubmit`、`PostToolUse`、`PostToolUseFailure`、`SessionEnd` | 用户输入了内容、获批的工具已运行或失败、CLI 已退出 | 已回答 |
| `Notification`，类型为 `idle_prompt` | “Claude 大约 60 秒前已完成回复，而你此后一直没有输入” | 忽略 |

大多数配置出错的地方就在 `idle_prompt` 这一行。它的消息读起来正是你想要的，但它会跟在任何一条没人接着输入的回复后面，所以把它当成问题，几乎每一轮之后都会触发提醒（在 Foxl 里，就是一条“待你处理”条目和一条手机通知）。我们注册 `Notification` 钩子时只用 `permission_prompt` 这个 matcher，脚本还会再检查一遍类型。对于 `Stop` 的判断，我们会先去掉末尾的空白、Markdown 强调符号、反引号、右括号和引号再检查，同时也接受中日韩输入法打出的全角 `？`。

Claude Code 当前的文档还列出了一个专门的 `PermissionRequest` 事件，以及比这里更多的通知类型。我们没有核实 2.1.296 是否已经有这个较新的事件。`permission_prompt` 仍在当前的列表中，而它六秒的等待，正好能过滤掉有人在屏幕前直接回答的对话框。把这些钩子添加到 Claude Code 的用户级设置文件中，放在同一事件已配置的钩子之后，这样现有的钩子会继续运行：

```
{
  "hooks": {
    "PreToolUse": [{ "matcher": "AskUserQuestion",
      "hooks": [{ "type": "command", "command": "report-prompt", "timeout": 10 }] }],
    "Notification": [{ "matcher": "permission_prompt",
      "hooks": [{ "type": "command", "command": "report-prompt", "timeout": 10 }] }],
    "Stop": [{ "hooks": [{ "type": "command", "command": "report-prompt", "timeout": 10 }] }]
  }
}
```

这些钩子都调用同一个小脚本，也就是上报命令（上面的 `report-prompt`），它从环境中读取该任务的凭证。表示用户已经回答的事件，即 `UserPromptSubmit` 及其余几个（关闭事件），也以同样的方式注册，只是多了一道下文介绍的防护。

## 怎样写钩子，才能让它给智能体的开销尽可能小？

上报钩子在智能体自己的循环里运行，所以它的任何错误都会变成智能体的问题。我们最后总结出五条规则，每一条都来自文档或 2.1.296 包：

- **不要在 stdout 上输出任何内容。**对大多数事件，Claude Code 只会记录钩子的 stdout，但对于 `UserPromptSubmit` 和其他少数几个事件，“Claude Code 会把纯文本 stdout 作为 Claude 可以看到并据此行动的上下文加入”。上报脚本输出的一行状态信息，就会变成提示词的一部分。
- **始终以 0 退出。**在可以阻止操作的事件上，退出码 2 会阻止该操作，其他非零退出码则会显示一条错误提示。在我们的脚本里，网络故障的代价只是 stderr 上的一行输出，别无其他。
- **同步运行，并设置较短的超时。**钩子可以用 `"async": true` 异步运行，但我们固定的这个版本每当一个异步钩子完成，都会在终端里打印一行；钩子在每次工具调用后都运行时，这就成了噪音。我们的钩子是同步的，设为 `timeout: 10`，脚本给自己的请求三秒时间，稍后自行退出。在 Claude Code 上，实际开销是每次回复结束时启动一次 Node.js、发一个请求；即使 Foxl 的服务器无法访问，最多也只是大约三秒。
- **在 shell 里拦住高频事件。**`PostToolUse` 在每次工具调用时都会触发。上报命令在上报一个未关闭的提示时，会创建一个标记文件。关闭钩子只在这个文件存在时才启动 Node.js，否则就把 stdin 读空，让 CLI 永远不会遇到断开的管道：```
  if [ -e /tmp/foxl-agent-prompt/open ]; then exec report-prompt; else cat >/dev/null; fi
  ```在没有未关闭的提示时，每次工具调用的开销就只是一次 `test -e`。
- **不要把凭证放在命令行里。**钩子继承 CLI 的环境，所以令牌从环境变量中读取，永远不会出现在 `ps` 里。

## Codex、OpenCode 和 Hermes 怎么表达这一点？

每个 CLI 都有自己的扩展点；我们按照镜像中固定的版本逐一读取了它们。Codex 有一个钩子引擎，接受 Claude Code 的钩子格式。OpenCode 和 Hermes（Nous Research 的智能体 CLI）在终端界面中不运行 shell 钩子；它们加载插件，而我们的插件会把一个规范化的事件交给同一个上报命令。AWS 的编程 CLI Kiro 则没有任何可以监听的事件。

| CLI（固定版本） | 提问 | 已回答 | 不上报 |
| --- | --- | --- | --- |
| Codex 0.162.1，系统级 `hooks.json` | `PermissionRequest`；`last_assistant_message` 以 `?` 结尾的 `Stop` | `UserPromptSubmit`、`PostToolUse`、`Interrupt`、`SessionEnd` | Plan 模式下的提问对话框 |
| OpenCode 1.19.0，服务器插件 | `question.asked`、`permission.asked`；回复以 `?` 结尾时的 `session.status` idle | `question.replied` 或 `rejected`、`permission.replied`、`session.status` busy | - |
| Hermes 0.19.0，Python 插件 | 针对 `clarify` 的 `pre_tool_call`、`pre_approval_request`、以 `?` 结尾的 `post_llm_call` | 针对 `clarify` 的 `post_tool_call`、`post_approval_response`、`pre_llm_call`、被中断的 `on_session_end` | - |
| Kiro 2.29.1 | 无 | - | 全部：没有提问或审批事件，它的 `Stop` 也不带回复文本 |

**Codex。**Codex 在运行你自己配置中的钩子之前会先审查它们：新增或修改过的钩子“会被标记为待审查，在获得信任之前一律跳过”。托管钩子设置在系统级配置中，按策略被信任，所以我们用这种方式安装自己的钩子。上报命令本身只安装在 Foxl Code 要求上报提示的终端里。在 0.162.1 上实测，非交互式的 `codex exec` 也会运行托管钩子，并且每次工具调用都会在输出中打印两行相关信息，所以对于 Foxl Code 用 `codex exec` 启动的运行，我们关闭了钩子。从 0.162.1 的源码中可以读到，Codex 的终端界面会显示任何运行超过 300 毫秒的钩子，所以上报命令会先判断是否有提示、写好标记文件，再把网络请求交给一个分离出去的自身副本。

Codex 的 Plan 模式提问对话框是有意不上报的，因为它的 `PreToolUse` 钩子在 Codex 判断这个问题是否被允许之前就会触发，而在 Plan 模式之外，Codex 会拒绝这个问题。在 0.162.1 上实测，钩子带着问题触发了，随后 Codex 回答“request\_user\_input is unavailable in Default mode”。Foxl Code 的终端以 Default 模式启动，所以上报这个钩子，就会把一个从来没有人被问到的问题推送到你的手机上。代价是，处于 Plan 模式的 Codex 终端，其提问对话框不会进入“待你处理”。

**OpenCode。**我们的插件监听 OpenCode 的事件总线。提问相关的事件并不在 OpenCode 插件文档的事件列表里，而是在 v1.19.0 的 schema 源码中，我们读的就是那里。idle 状态表示一轮结束，如果这一轮最后的回复以 `?` 结尾，就算作一个问题。当提问或权限对话框打开时，状态变化会被忽略，所以智能体在对话框前后变为 busy 或 idle，都不会把它关掉。来自子智能体会话的事件会被跳过。

**Hermes。**和名字给人的印象相反，Hermes 的 `on_session_end` 在 0.19.0 上会在每一轮结束时、紧跟着回复触发。当我们的插件把它当作关闭事件时，它就撤回了这一轮刚刚提出的问题：12 个问题中有 7 个被立即关闭；还有 1 个问题在它自己的关闭事件之后才到达服务器，于是一直保持打开，再也没有东西能关闭它。现在只有被中断的会话结束才算作关闭，而且上报会按 Hermes 产生它们的顺序逐个发出。

**Kiro** 不在支持范围内，因为它的钩子没有提问或审批事件，它的 stop 数据也不带回复文本。钩子既无法打开一个提示，也无法区分问题和回答。唯一的信号是一个格式未公开的内部会话文件，我们选择不去解析它。

## Foxl 知道之后会发生什么？

每条上报都发往 Foxl Code 服务器上的同一个端点，用该任务自己的凭证进行身份验证，并在同一个“待你处理”列表中打开或关闭一个条目，这个列表也收纳着智能体的其他问题。任务会被标记为等待你的回答，如图所示。

![Foxl Code 的 Work 页面。在 Needs you 栏中，一张卡片写着“Claude Code is asking”，附有一个关于某个故意失败的作业的是非题，以及带 Send 按钮的回答框。第二张卡片同样写着“Claude Code is asking”，列出了一个问题和三个选项，后面是“Answer this in the terminal.”和一个 Show terminal 按钮。在 In flight 栏中，有两个任务显示“Waiting for your answer”。](<https://foxl.ai/blog/coding-agent-waiting-for-input-needs-you.webp>)

_图 1. 来自该拉取请求构建的 Foxl Code“工作”页面，使用的是示例问题。回复里的问题可以在这里回答；对话框则要在终端里回答。_

回复末尾的问题可以在“待你处理”或手机上回答。Foxl 会把答案作为一行输入到该任务的终端中，一秒后再单独发送回车键，因为包含回车的单个数据块会被当作粘贴，永远不会被提交。对于提问或权限对话框，Foxl 从不猜测终端界面期望的按键：它在“待你处理”中的卡片会列出选项，提示你在终端中回答，并提供一个打开终端的按钮。在终端中回答后，卡片会通过同样的钩子关闭。

只有可能被忽略的提示才会通知手机。在 Foxl 得知 15 秒后仍处于打开状态的提问或权限对话框，会发送一条通知，因为盯着终端的人几秒内就会回答。Claude Code 的权限对话框会在打开约六秒后才上报，所以它的通知大约在第 21 秒到达。以问题结尾的回复会显示在“待你处理”中，但不会推送通知，因为很多回复都以问题结尾。2026-10-21 的那个问题就属于这一类，所以现在它会出现在“待你处理”和任务上，但仍然不会让手机响起。

![一张从左到右的流程图。四个 CLI（Claude Code、Codex、OpenCode 和 Hermes）各自通过自己的钩子或插件，向智能体所在机器内的一个小命令上报。这个命令用任务的凭证，在 Foxl Code 的服务器上打开或关闭一个提示。服务器把它显示在“待你处理”和任务上，只有当对话框在上报 15 秒后仍然打开时才通知手机。](<https://foxl.ai/blog/coding-agent-waiting-for-input-flow.png>)

_图 2. 四个 CLI 通过同一个命令上报；只有当对话框在上报 15 秒后仍无人回答时，手机才会收到通知。_

## 还有哪些没有验证？

在我们的测试中，真实的上报命令跑过了每个固定版本 CLI 的数据，插件在 Node.js 和 Python 下按固定的事件格式运行过，服务器端点也在 Cloudflare 运行时的本地副本中运行过。还没有做到的是：

- **来自真实云端终端的线上提问。**截至 2026-10-23，生产环境的表中还没有来自这类终端的提示，所以在线上终端上的端到端检查仍有待完成。
- **Claude Code 实际运行这些钩子。**Claude Code 的细节，包括六秒的延迟和异步钩子打印的那一行，都是从 2.1.296 包中读取的，而不是在运行中的终端上看到的。我们有一个让该版本搭配这些钩子运行的测试，但还没有运行过。
- **其他 CLI 的终端。**Codex、OpenCode 和 Hermes 的若干细节是从源码中读到的，包括 OpenCode 会以这些确切的名称发出提问事件；实际运行过的只有 `codex exec`。

如果你在自己的终端里运行智能体，最值得照搬的是 Claude Code 的钩子表。把这三个钩子接到任何能联系到你的地方，忽略 `idle_prompt`，不输出任何内容，以 0 退出，并在发送提醒前等几秒。关于 Foxl Code 的终端这一侧，我们的文章[为什么每次按键曾经都发到了错误的机器上](<https://foxl.ai/blog/a-citation-is-not-a-measurement>)（英文）介绍了输入究竟是怎样到达远程智能体的。

## 参考资料与延伸阅读

1. [Claude Code：钩子参考](<https://code.claude.com/docs/en/hooks>) - Documentation
2. [Codex：钩子](<https://learn.chatgpt.com/docs/hooks>) - Documentation
3. [OpenCode：插件](<https://opencode.ai/docs/plugins/>) - Documentation
4. [Hermes Agent（Nous Research）](<https://github.com/NousResearch/hermes-agent>) - Reference
5. [Foxl v0.9.3-beta.5 发布](<https://github.com/foxl-ai/foxl/releases/tag/v0.9.3-beta.5>) - Release