Skip to content

Commit 24408f9

Browse files
authored
update PermissionResult to match latest control protocol (#209)
Recreation of #174, with signed commits
1 parent 7035858 commit 24408f9

File tree

3 files changed

+50
-9
lines changed

3 files changed

+50
-9
lines changed

src/claude_agent_sdk/_internal/query.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,18 @@ async def _handle_control_request(self, request: SDKControlRequest) -> None:
213213

214214
# Convert PermissionResult to expected dict format
215215
if isinstance(response, PermissionResultAllow):
216-
response_data = {"allow": True}
216+
response_data = {"behavior": "allow"}
217217
if response.updated_input is not None:
218-
response_data["input"] = response.updated_input
219-
# TODO: Handle updatedPermissions when control protocol supports it
218+
response_data["updatedInput"] = response.updated_input
219+
if response.updated_permissions is not None:
220+
response_data["updatedPermissions"] = [
221+
permission.to_dict()
222+
for permission in response.updated_permissions
223+
]
220224
elif isinstance(response, PermissionResultDeny):
221-
response_data = {"allow": False, "reason": response.message}
222-
# TODO: Handle interrupt flag when control protocol supports it
225+
response_data = {"behavior": "deny", "message": response.message}
226+
if response.interrupt:
227+
response_data["interrupt"] = response.interrupt
223228
else:
224229
raise TypeError(
225230
f"Tool permission callback must return PermissionResult (PermissionResultAllow or PermissionResultDeny), got {type(response)}"

src/claude_agent_sdk/types.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,42 @@ class PermissionUpdate:
7070
directories: list[str] | None = None
7171
destination: PermissionUpdateDestination | None = None
7272

73+
def to_dict(self) -> dict[str, Any]:
74+
"""Convert PermissionUpdate to dictionary format matching TypeScript control protocol."""
75+
result: dict[str, Any] = {
76+
"type": self.type,
77+
}
78+
79+
# Add destination for all variants
80+
if self.destination is not None:
81+
result["destination"] = self.destination
82+
83+
# Handle different type variants
84+
if self.type in ["addRules", "replaceRules", "removeRules"]:
85+
# Rules-based variants require rules and behavior
86+
if self.rules is not None:
87+
result["rules"] = [
88+
{
89+
"toolName": rule.tool_name,
90+
"ruleContent": rule.rule_content,
91+
}
92+
for rule in self.rules
93+
]
94+
if self.behavior is not None:
95+
result["behavior"] = self.behavior
96+
97+
elif self.type == "setMode":
98+
# Mode variant requires mode
99+
if self.mode is not None:
100+
result["mode"] = self.mode
101+
102+
elif self.type in ["addDirectories", "removeDirectories"]:
103+
# Directory variants require directories
104+
if self.directories is not None:
105+
result["directories"] = self.directories
106+
107+
return result
108+
73109

74110
# Tool callback types
75111
@dataclass

tests/test_tool_callbacks.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ async def allow_callback(
9090
# Check response was sent
9191
assert len(transport.written_messages) == 1
9292
response = transport.written_messages[0]
93-
assert '"allow": true' in response
93+
assert '"behavior": "allow"' in response
9494

9595
@pytest.mark.asyncio
9696
async def test_permission_callback_deny(self):
@@ -125,8 +125,8 @@ async def deny_callback(
125125
# Check response
126126
assert len(transport.written_messages) == 1
127127
response = transport.written_messages[0]
128-
assert '"allow": false' in response
129-
assert '"reason": "Security policy violation"' in response
128+
assert '"behavior": "deny"' in response
129+
assert '"message": "Security policy violation"' in response
130130

131131
@pytest.mark.asyncio
132132
async def test_permission_callback_input_modification(self):
@@ -164,7 +164,7 @@ async def modify_callback(
164164
# Check response includes modified input
165165
assert len(transport.written_messages) == 1
166166
response = transport.written_messages[0]
167-
assert '"allow": true' in response
167+
assert '"behavior": "allow"' in response
168168
assert '"safe_mode": true' in response
169169

170170
@pytest.mark.asyncio

0 commit comments

Comments
 (0)