Hook `async` property silently ignored — postToolUse hooks block tool completion
- 主要语言
- Shell
- 星标
- 11.2k
- 派生
- 1.9k
- 平均合并
- 14 小时 16 分钟
- 30 天内合并 PR
- 6
描述
## Describe the bug
The `async` property on command hooks in `hooks.json` is silently ignored. Hooks with `"async": true` still block tool completion because `executeHooks()` unconditionally `await`s every hook in a sequential `for` loop.
Claude Code [documents `async`](https://docs.anthropic.com/en/docs/claude-code/hooks) as a supported hook field:
> **`async`** — If `true`, runs in the background without blocking.
Since plugins share the same `hooks.json` format across both Claude Code and Copilot CLI, authors reasonably expect `"async": true` to work. Instead, it is parsed by the Zod schema (via `.passthrough()`) but never read by the execution path.
## Impact
In a real session, a `postToolUse` hook with `"async": true` blocked for **13.5 seconds** on first invocation because it triggered a package auto-update (resolving 419 packages via `uv`). This added ~14s of latency to the first tool call of the session despite the hook being marked async.
**Timeline from session events:**
```
18:57:09.756 hook.start postToolUse
18:57:22.186 [hook stdout] Resolved 419 packages in 6.91s
18:57:22.803 [hook stdout] Installed 2 packages
18:57:23.216 hook.end ← 13.46s blocked
```
## Root cause
In `sdk/index.js`, the `executeHooks` function (`Yg` in minified source):
```javascript
async function executeHooks(hooks, input, logger, hookType, emitter) {
// ...
for (let hook of hooks) {
let result = await hook(input); // ← always awaits, ignores async flag
// ...
}
}
```
And in `processToolExecutionResult`:
```javascript
await executeHooks(this.getEffectiveHooks()?.postToolUse, ...) // blocks tool completion
```
The `async` property from the hook config is never propagated to the execution layer.
## Expected behavior
When a hook specifies `"async": true`, it should run in the background without blocking tool completion — matching Claude Code's documented behavior. Ideally also support `asyncRewake` for parity.
## Workaround
Plugin authors can append `&` to the shell command to background it manually, but this loses stdout/stderr capture and exit code handling.
## Environment
- Copilot CLI: 1.0.40-2
- OS: macOS (Darwin arm64)
## Related issues
- #2893 — hooks serialized + timeout fail-open (related hook execution model concerns)
贡献指南
调研方向
首先跟踪 sdk/index.js 中的 executeHooks 以及它从 processToolExecutionResult 发起的调用,然后检查 hook 配置如何传递到执行层。确认异步 hook 应如何在后台完成,以及输出、错误和工具完成应是什么样;完成的标准是异步 hook 不再阻塞,同时同步 hook 保持当前行为不变。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- javascript
- 领域
- cli
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 48/100