anthropics / anthropics/claude-agent-sdk-typescript

Aborted tool calls are indistinguishable from user denials: expose toolDenialKind and reword the abort substitution

Offen
#424 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
bug enhancement
Vorherrschende Sprache
Shell
Sterne
1.8k
Forks
226
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

## Summary

When a turn is aborted (host `AbortController`, timeout, etc.), the CLI synthesizes `tool_result` blocks for tool uses that were in flight and persists them into the session transcript with the content:

> The user doesn't want to take this action right now. STOP what you are doing and wait for the user to tell you how to proceed.

This is the same string a genuine permission denial produces. On the next `resume` of that session, the model reads these synthesized results as real human refusals — in our host (a chat UI where turns are capped by a host-side timeout) the agent apologized for a refusal that never happened and stopped work three separate times waiting for permission it already had.

The CLI *already records* the distinction: persisted `user` entries carry a top-level `toolDenialKind` field (`user-rejected` | `permission-rule` | `cancelled` | `interrupted`). But:

1. **The SDK does not expose `toolDenialKind`** — `SDKUserMessage` has no such field, so a host consuming the stream (or reading messages back via `getSessionMessages`) cannot distinguish "a human said no" from "the turn was torn down" without parsing prose.
2. **The substituted content itself is model-visible and misleading.** The metadata is right (`cancelled`/`interrupted`), but the text the model actually sees on resume claims user intent that never existed.

## Requests

1. Surface `toolDenialKind` on the relevant SDK message types (stream + transcript readers), so hosts can programmatically tell aborts from denials.
2. Consider rewording the abort-path substitution to something intent-neutral, e.g. "This tool call was interrupted before it completed (turn cancelled). It was not denied by the user." — keeping the current wording only for actual `user-rejected` denials.

## Environment

- `@anthropic-ai/claude-agent-sdk` 0.3.223 (also verified against 0.3.238 typings)
- Claude Code CLI 2.1.241
- Observed via `query()` with a host-side `AbortController` and `resume`

## Repro sketch

1. Start a `query()` turn that runs a slow allowlisted tool (so `canUseTool` is never consulted).
2. Abort the `AbortController` while tool uses are pending/in flight.
3. Inspect the persisted session JSONL: the unresolved tool uses have `tool_result` entries with `is_error: true` and the "user doesn't want to take this action" content (a tool already executing may instead persist its kill result, e.g. "Exit code 137").
4. `resume` the session and ask the model to continue: it treats the substituted results as user refusals.

Happy to provide fuller traces if useful.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.