fix(sdk): support "auto" permission mode (#9003)

* fix(sdk-python): support "auto" permission mode

* refactor(sdk-python): derive permission-mode validation from PermissionMode type

* fix(sdk-java): support "auto" permission mode

* refactor(sdk-python): derive auth-type and effort validation from type aliases

* chore: rerun CI
This commit is contained in:
Yan Shen authored and GitHub committed 2026-08-22 14:25:40 +00:00
1 parent 39378ac0a4
commit 867ded5bd5
9 files changed
+59 -33

No files matched your search

+22 -22
View File
@@ -139,28 +139,28 @@ with query_sync(
### `QueryOptions`
| Option | Type / values | Description |
| -------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `cwd` | `str` | Working directory for the CLI process. |
| `model` | `str` | Model override for this SDK session. |
| `path_to_qwen_executable` | `str` | `qwen`, an explicit binary path, or a `.js` CLI bundle. |
| `permission_mode` | `default`, `plan`, `auto-edit`, `yolo` | Tool execution approval mode. `yolo` auto-approves all tools; use it only in trusted or sandboxed environments. |
| `can_use_tool` | async callback | Custom permission callback for tool requests. |
| `env` | `dict[str, str]` | Extra environment variables passed to the CLI process. |
| `system_prompt` | `str` | Override the system prompt. |
| `append_system_prompt` | `str` | Append extra instructions to the system prompt. |
| `debug` | `bool` | Forward CLI stderr to stderr when no `stderr` hook exists. |
| `max_session_turns` | `int` | Maximum turns before the CLI ends the session. |
| `core_tools` | `list[str]` | Restrict the available tool set. |
| `exclude_tools` | `list[str]` | Exclude matching tools. |
| `allowed_tools` | `list[str]` | Allow matching tools without callback approval. |
| `auth_type` | `openai`, `anthropic`, `qwen-oauth`, `gemini`, `vertex-ai` | Authentication mode passed to the CLI. |
| `include_partial_messages` | `bool` | Emit partial assistant stream events. |
| `resume` | UUID string | Resume a known session id. |
| `continue_session` | `bool` | Continue the latest CLI session. |
| `session_id` | UUID string | Start or correlate a session with a known id. |
| `timeout` | mapping | Timeouts in seconds. |
| `stderr` | callable | Receives CLI stderr lines. |
| Option | Type / values | Description |
| -------------------------- | ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cwd` | `str` | Working directory for the CLI process. |
| `model` | `str` | Model override for this SDK session. |
| `path_to_qwen_executable` | `str` | `qwen`, an explicit binary path, or a `.js` CLI bundle. |
| `permission_mode` | `default`, `plan`, `auto-edit`, `auto`, `yolo` | Tool execution approval mode. `auto` lets an LLM classifier approve tool calls; `yolo` auto-approves all tools; use it only in trusted or sandboxed environments. |
| `can_use_tool` | async callback | Custom permission callback for tool requests. |
| `env` | `dict[str, str]` | Extra environment variables passed to the CLI process. |
| `system_prompt` | `str` | Override the system prompt. |
| `append_system_prompt` | `str` | Append extra instructions to the system prompt. |
| `debug` | `bool` | Forward CLI stderr to stderr when no `stderr` hook exists. |
| `max_session_turns` | `int` | Maximum turns before the CLI ends the session. |
| `core_tools` | `list[str]` | Restrict the available tool set. |
| `exclude_tools` | `list[str]` | Exclude matching tools. |
| `allowed_tools` | `list[str]` | Allow matching tools without callback approval. |
| `auth_type` | `openai`, `anthropic`, `qwen-oauth`, `gemini`, `vertex-ai` | Authentication mode passed to the CLI. |
| `include_partial_messages` | `bool` | Emit partial assistant stream events. |
| `resume` | UUID string | Resume a known session id. |
| `continue_session` | `bool` | Continue the latest CLI session. |
| `session_id` | UUID string | Start or correlate a session with a known id. |
| `timeout` | mapping | Timeouts in seconds. |
| `stderr` | callable | Receives CLI stderr lines. |
Use only one of `resume`, `continue_session`, or `session_id` in a request. The
SDK raises `ValidationError` if these session options are combined.
+1
View File
@@ -220,6 +220,7 @@ The SDK supports different permission modes for controlling tool execution:
- **`default`**: Write tools are denied unless approved via `canUseTool` callback or in `allowedTools`. Read-only tools execute without confirmation.
- **`plan`**: Blocks all write tools, instructing AI to present a plan first.
- **`auto-edit`**: Auto-approve edit tools (`edit`, `write_file`, `notebook_edit`) while other tools require confirmation.
- **`auto`**: An LLM classifier approves tool calls.
- **`yolo`**: All tools execute automatically without confirmation.
### Session Event Consumers and Assistant Content Consumers
@@ -19,6 +19,10 @@ public enum PermissionMode {
* Auto-edit permission mode.
*/
AUTO_EDIT("auto-edit"),
/**
* Auto permission mode.
*/
AUTO("auto"),
/**
* YOLO permission mode.
*/
@@ -230,7 +230,7 @@ export interface SDKPartialAssistantMessage {
parent_tool_use_id: string | null;
}
export type PermissionMode = 'default' | 'plan' | 'auto-edit' | 'yolo';
export type PermissionMode = 'default' | 'plan' | 'auto-edit' | 'auto' | 'yolo';
/**
* TODO: Align with `ToolCallConfirmationDetails`
@@ -13,6 +13,7 @@ public class PermissionModeTest {
assertEquals("default", PermissionMode.DEFAULT.getValue());
assertEquals("plan", PermissionMode.PLAN.getValue());
assertEquals("auto-edit", PermissionMode.AUTO_EDIT.getValue());
assertEquals("auto", PermissionMode.AUTO.getValue());
assertEquals("yolo", PermissionMode.YOLO.getValue());
}
+3 -2
View File
@@ -141,8 +141,9 @@ Common fields:
- `path_to_qwen_executable`: `qwen`, an absolute binary path, or a `.js` CLI
bundle
- `model`: model override for this session
- `permission_mode`: one of `default`, `plan`, `auto-edit`, or `yolo`; `yolo`
auto-approves all tools, so use it only in trusted or sandboxed environments
- `permission_mode`: one of `default`, `plan`, `auto-edit`, `auto`, or `yolo`;
`auto` lets an LLM classifier approve tool calls; `yolo` auto-approves all
tools, so use it only in trusted or sandboxed environments
- `env`: extra environment variables passed to the CLI process
- `system_prompt` / `append_system_prompt`: override or extend the system
prompt
@@ -15,7 +15,7 @@ from typing import (
from typing_extensions import NotRequired
PermissionMode: TypeAlias = Literal["default", "plan", "auto-edit", "yolo"]
PermissionMode: TypeAlias = Literal["default", "plan", "auto-edit", "auto", "yolo"]
AuthType: TypeAlias = Literal[
"openai",
"anthropic",
@@ -3,18 +3,22 @@
from __future__ import annotations
from collections.abc import Callable
from typing import get_args
from uuid import RFC_4122, UUID
from .errors import ValidationError
from .types import (
AuthType,
Effort,
PermissionMode,
QueryOptions,
_validate_can_use_tool_callable,
_validate_stderr_callable,
)
_VALID_PERMISSION_MODES = {"default", "plan", "auto-edit", "yolo"}
_VALID_AUTH_TYPES = {"openai", "anthropic", "qwen-oauth", "gemini", "vertex-ai"}
_VALID_EFFORTS = {"low", "medium", "high", "xhigh", "max"}
_VALID_PERMISSION_MODES = set(get_args(PermissionMode))
_VALID_AUTH_TYPES = set(get_args(AuthType))
_VALID_EFFORTS = set(get_args(Effort))
_RESERVED_CLI_FLAGS = frozenset(
@@ -87,19 +91,19 @@ def validate_query_options(options: QueryOptions) -> None:
):
raise ValidationError(
f"Invalid permission_mode: {options.permission_mode!r}. "
"Expected one of: default, plan, auto-edit, yolo."
f"Expected one of: {', '.join(get_args(PermissionMode))}."
)
if options.auth_type and options.auth_type not in _VALID_AUTH_TYPES:
raise ValidationError(
f"Invalid auth_type: {options.auth_type!r}. "
"Expected one of: openai, anthropic, qwen-oauth, gemini, vertex-ai."
f"Expected one of: {', '.join(get_args(AuthType))}."
)
if options.effort and options.effort not in _VALID_EFFORTS:
raise ValidationError(
f"Invalid effort: {options.effort!r}. "
"Expected one of: low, medium, high, xhigh, max."
f"Expected one of: {', '.join(get_args(Effort))}."
)
_validate_optional_callable(options.can_use_tool, _validate_can_use_tool_callable)
@@ -1,5 +1,6 @@
from __future__ import annotations
import re
from typing import Any, cast
import pytest
@@ -74,8 +75,22 @@ def test_accepts_canonical_session_id_in_either_case() -> None:
validate_query_options(QueryOptions(session_id=VALID_UUID.upper()))
@pytest.mark.parametrize(
"mode",
["default", "plan", "auto-edit", "auto", "yolo"],
)
def test_accepts_valid_permission_modes(mode: str) -> None:
validate_query_options(QueryOptions.from_mapping({"permission_mode": mode}))
def test_rejects_invalid_permission_mode() -> None:
with pytest.raises(ValidationError, match="Invalid permission_mode"):
with pytest.raises(
ValidationError,
match=re.escape(
"Invalid permission_mode: 'unsafe-mode'. "
"Expected one of: default, plan, auto-edit, auto, yolo."
),
):
validate_query_options(
QueryOptions.from_mapping({"permission_mode": "unsafe-mode"})
)