Visitar URL original
feat: add permission_prompts option by model-bender · Pull Request #1477 · anthropics/claude-agent-sdk-python · GitHub
Skip to content

feat: add permission_prompts option - #1477

Open
model-bender wants to merge 1 commit into
anthropics:mainfrom
model-bender:feat/permission-prompts
Open

model-bender wants to merge 1 commit into
anthropics:mainfrom
model-bender:feat/permission-prompts

Conversation

@model-bender

Copy link
Copy Markdown

Re-opened from a new fork with the same commits as #1324, which closed when its fork was deleted. Earlier review is on #1324.

Summary

Adds ClaudeAgentOptions.permission_prompts, the Python counterpart of the TypeScript SDK's permissionPrompts, plus a PermissionPrompts type alias (Literal["host", "none"]) exported next to PermissionMode.

  • "host" (the CLI's default): this process answers prompts, through can_use_tool or permission_prompt_tool_name.
  • "none": nobody does. The permission mode (including auto mode's classifier), rules and hooks still decide, anything that would otherwise prompt is denied at once with a message telling Claude the session has no approval surface, and can_use_tool is never called. Useful for unattended runs, where a prompt nobody can answer is worse than a clear denial.

When set, the SDK passes --permission-prompts <value>; when unset, nothing is added, so existing behaviour is unchanged. This follows the TypeScript SDK (checked against @anthropic-ai/claude-agent-sdk 0.3.283: if (permissionPrompts) args.push("--permission-prompts", permissionPrompts)). Until now Python users needed extra_args={"permission-prompts": "none"}.

Verification

  • Real model calls. Bundled CLI version 2.1.283 through ClaudeSDKClient with model="haiku", permission_mode="default", a can_use_tool callback that approves and records every call, and a prompt asking Claude to create a file with the Write tool:

    callback called for file written tool error Claude sees
    default ['Write'] yes none
    permission_prompts="host" ['Write'] yes none
    permission_prompts="none" [] no "Permission for this tool use was denied. It requires approval, and this session has no approval surface ..."

    With "none" the run still finishes cleanly (is_error=False; Claude replies that the tool was denied).

  • New unit test test_build_command_permission_prompts (tests/test_transport.py). With the src/ changes stashed it fails (TypeError: ... unexpected keyword argument 'permission_prompts'); with them, it passes.

  • New e2e tests in e2e-tests/test_permission_prompts.py, laid out like the verbatim_prompts ones: a control where the write asks can_use_tool and lands, then ClaudeSDKClient and query() runs with permission_prompts="none" where it doesn't. 3 passed against the real API.

  • ruff check, ruff format --check, mypy src/ scripts/ are clean. pytest tests/: 1588 passed, 6 skipped on Python 3.11, 3.12 and 3.13 (1587 passed, 7 skipped on 3.10, where the extra skip is a build test that needs tomllib), and again with mcp==1.23.0.

The TypeScript SDK's permissionPrompts option has no Python counterpart,
so a host that wants prompts denied rather than answered had to reach for
extra_args. With permission_prompts="none" the CLI runs with
--permission-prompts none: the permission mode, rules and hooks still
decide, anything that would prompt is denied at once, and can_use_tool is
never called. "host" is the CLI's default and is passed through as given.

As in the TypeScript SDK, the flag is sent only when the option is set.
The e2e tests follow the verbatim_prompts layout: a control where the
write asks can_use_tool and lands, then ClaudeSDKClient and query() runs
with "none" where it doesn't.
the default handler.
"""

permission_prompts: PermissionPrompts | None = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ClaudeAgentOptions is a positional dataclass. Inserting this field before cwd shifts every subsequent constructor argument, so existing positional calls can bind a path to permission_prompts and pass it as --permission-prompts instead of setting the working directory. Could this new optional field be appended at the end (or made keyword-only) to preserve existing positional calls?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment. You're right that it shifts positional binding. I kept it next to permission_prompt_tool_name because that's how options get added here: strict_mcp_config, include_hook_events, forward_subagent_text, resume_drops_turn and verbatim_prompts all went in mid-class, and every ClaudeAgentOptions(...) call in the repo and docs uses keyword arguments. Making the class kw_only would be the real fix, but it changes the whole public constructor, so I'd keep that out of this PR. Happy to move the field to the end if the maintainers prefer.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining the existing convention, @model-bender. I agree making the entire dataclass keyword-only would be a separate, broader change. My remaining concern is that this is a public positional dataclass, so adding a field before cwd still silently changes the meaning of existing positional calls, even if the repository's own examples all use keywords. Appending this one field at the end preserves both those calls and every existing keyword call, with no change to the CLI behavior. Happy to leave that compatibility tradeoff to the maintainers; appreciate the discussion.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants