anthropics / anthropics/claude-agent-sdk-typescript
Aborted tool calls are indistinguishable from user denials: expose toolDenialKind and reword the abort substitution
- 主要語言
- Shell
- 星號
- 1.8k
- 分支
- 226
- PR 合併指標
- 30 天內沒有已合併 PR
描述
## 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.
貢獻指南
這個儲存庫沒有索引到貢獻指南
評估
這個 Issue 還沒有評估資料。