anthropics / anthropics/claude-code

[FEATURE] PermissionRequest hook input should include tool_use_id (per-call id) to correlate identical simultaneous prompts

Open
#95,116 0 comments 0 reactions 0 assignees View on GitHub
area:hooks enhancement
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### Preflight Checklist

- [x] I have searched existing requests. #13938 and #83748 asked for the same field and were both auto-closed as stale, not declined; the bot asked for a new issue that references them.
- [x] This is a single feature request (not multiple features)

### Problem Statement

`PermissionRequest` is the only tool-call hook event whose input cannot be tied to the call it gates. `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionDenied` and `PostToolBatch` all carry `tool_use_id`. The hooks reference still says PermissionRequest receives `tool_name` and `tool_input` "like PreToolUse hooks, but without `tool_use_id`".

This breaks external permission brokers that mirror a pending approval to another surface (a web or mobile card) and must retire that card when the user answers in the terminal instead. The broker learns about the terminal answer from the transcript (`tool_use.id` / `tool_result.tool_use_id`), but it has no identifier linking that id to the `PermissionRequest` it parked.

The unrecoverable case is **two simultaneous calls with identical input** (for example the same no-argument tool, or the same Bash command twice in one turn):
- Their `PermissionRequest` payloads have the same `session_id`, `tool_name`, `tool_input` and `prompt_id`, so nothing in the payload tells them apart.
- `prompt_id` identifies the user prompt, not the individual tool call, so it cannot separate calls made in the same turn.
- Pairing `PermissionRequest` with `PreToolUse` by session, prompt, tool name and input fails for exactly this case. Pairing by arrival order is a guess.

**Evidence (measured on Claude Code 2.1.273, Linux, isolated settings via `--settings` and `--setting-sources ""`):** a hook teed stdin for both events.
- `PermissionRequest` top-level keys: `session_id, transcript_path, cwd, scratchpad_dir, prompt_id, permission_mode, effort, hook_event_name, tool_name, tool_input, permission_suggestions`. There is **no `tool_use_id`**.
- `PreToolUse` top-level keys: the same set without `permission_suggestions`, **plus `tool_use_id`**.
- This matches the 2.1.221 capture in #83748, so the gap has persisted across releases. We have not re-captured on builds after 2.1.273; the current hooks reference still documents the omission.

**Impact:** a correct broker has to fail closed. When it cannot prove which parked request a terminal answer resolved, it resolves neither. Both remote cards stay pending until the hook times out (for us, 30 minutes for unattended sessions and up to 24 hours otherwise), even though the user already answered. The alternative, guessing, can clear the card for the call that is still waiting and leave the answered one pending. We will not ship that.

### Proposed Solution

Add `tool_use_id` to the `PermissionRequest` hook input. It should be the same value that later appears as `tool_use.id` in the transcript and as `tool_use_id` in `PreToolUse`/`PostToolUse` for that call.

If the id cannot exist yet when the prompt is raised, a stable per-call identifier that is **deterministically correlated** with the later `tool_use_id` would also work, as long as the correlation is exposed (for example, the same identifier also appears on `PreToolUse`/`PostToolUse`). It must be unique per call, including for identical inputs within one prompt.

### Alternative Solutions

- Matching on `session_id` + `tool_name` + normalized `tool_input` (the hashing workaround mentioned on #13938) is ambiguous by construction for identical calls.
- Pairing with `PreToolUse` by `prompt_id` and input has the same ambiguity; pairing by order is non-deterministic.
- A "resolved elsewhere" notification to the waiting `PermissionRequest` hook process (suggested on #13938) would also solve it, if it identifies the call.
- Today we fail closed and wait for the hook timeout.

### Priority

High - Significant impact on productivity

### Feature Category

Developer tools/SDK

### Use Case Example

1. An agent runs in Claude Code with a `PermissionRequest` hook that forwards approvals to a remote approval card, so a user away from the terminal can answer.
2. In one turn the agent issues two identical calls that both need approval. Two `PermissionRequest` hooks fire with indistinguishable payloads, and two cards are shown.
3. The user is at the terminal and approves one prompt there. The transcript records a `tool_result` for one `tool_use_id`.
4. With `tool_use_id` on `PermissionRequest`, the broker retires exactly the matching card, and the other stays correctly pending. Without it, the broker cannot know which card to retire, so both stay stale until timeout.

### Additional Context

- Related (stale-closed, same field): #13938, #83748.
- Measured version: 2.1.273. The capture payloads contained only session, path and command metadata; raw identifiers are omitted here.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the hooks reference and the PermissionRequest, PreToolUse, PostToolUse, and transcript event definitions mentioned in the issue. Trace how per-call identifiers are created and propagated, then verify that identical simultaneous calls receive distinct correlated identifiers in PermissionRequest and later hook or transcript events.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
40/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.