feat: add background, effort, permissionMode to AgentDefinition#782
Merged
feat: add background, effort, permissionMode to AgentDefinition#782
Conversation
Contributor
Author
E2E ProofScript"""E2E proof for PR #782: background, effort, permissionMode on AgentDefinition."""
import asyncio
import sys
from claude_agent_sdk import (
AgentDefinition,
ClaudeAgentOptions,
ClaudeSDKClient,
SystemMessage,
)
async def main():
results = []
# Test 1: AgentDefinition with effort field (string)
print("Test 1: effort field (string) accepted by CLI")
try:
options = ClaudeAgentOptions(
agents={
"effort-agent": AgentDefinition(
description="Agent with effort field",
prompt="You are a helpful assistant. Reply with one short sentence.",
effort="low",
)
},
max_turns=1,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Say hello in exactly 3 words")
found_agent = False
async for msg in client.receive_response():
if isinstance(msg, SystemMessage) and msg.subtype == "init":
agents = msg.data.get("agents", [])
found_agent = "effort-agent" in agents
print(f" Agents in init: {agents}")
break
print(f" {chr(39)}PASS{chr(39)} if found_agent else {chr(39)}FAIL{chr(39)}")
results.append(found_agent)
except Exception as e:
print(f" FAIL: {e}")
results.append(False)
# Test 2: AgentDefinition with effort as integer
print("Test 2: effort as integer accepted by CLI")
try:
options = ClaudeAgentOptions(
agents={
"effort-int-agent": AgentDefinition(
description="Agent with integer effort",
prompt="You are a helpful assistant. Reply with one short sentence.",
effort=32000,
)
},
max_turns=1,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Say goodbye in exactly 3 words")
found_agent = False
async for msg in client.receive_response():
if isinstance(msg, SystemMessage) and msg.subtype == "init":
agents = msg.data.get("agents", [])
found_agent = "effort-int-agent" in agents
print(f" Agents in init: {agents}")
break
results.append(found_agent)
except Exception as e:
print(f" FAIL: {e}")
results.append(False)
# Test 3: AgentDefinition with permissionMode
print("Test 3: permissionMode field accepted by CLI")
try:
options = ClaudeAgentOptions(
agents={
"perm-agent": AgentDefinition(
description="Agent with permissionMode",
prompt="You are a helpful assistant. Reply with one short sentence.",
permissionMode="bypassPermissions",
)
},
max_turns=1,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Say thanks in exactly 3 words")
found_agent = False
async for msg in client.receive_response():
if isinstance(msg, SystemMessage) and msg.subtype == "init":
agents = msg.data.get("agents", [])
found_agent = "perm-agent" in agents
print(f" Agents in init: {agents}")
break
results.append(found_agent)
except Exception as e:
print(f" FAIL: {e}")
results.append(False)
# Test 4: AgentDefinition with background=True
print("Test 4: background field accepted by CLI")
try:
options = ClaudeAgentOptions(
agents={
"bg-agent": AgentDefinition(
description="Background agent",
prompt="You are a helpful assistant. Reply with one short sentence.",
background=True,
)
},
max_turns=1,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Say yes in exactly 2 words")
found_agent = False
async for msg in client.receive_response():
if isinstance(msg, SystemMessage) and msg.subtype == "init":
agents = msg.data.get("agents", [])
found_agent = "bg-agent" in agents
print(f" Agents in init: {agents}")
break
results.append(found_agent)
except Exception as e:
print(f" FAIL: {e}")
results.append(False)
# Test 5: All three new fields combined
print("Test 5: all new fields combined accepted by CLI")
try:
options = ClaudeAgentOptions(
agents={
"full-agent": AgentDefinition(
description="Fully-configured agent",
prompt="You are a helpful assistant. Reply with one short sentence.",
background=True,
effort="high",
permissionMode="bypassPermissions",
)
},
max_turns=1,
)
async with ClaudeSDKClient(options=options) as client:
await client.query("Say wow in exactly 2 words")
found_agent = False
async for msg in client.receive_response():
if isinstance(msg, SystemMessage) and msg.subtype == "init":
agents = msg.data.get("agents", [])
found_agent = "full-agent" in agents
print(f" Agents in init: {agents}")
break
results.append(found_agent)
except Exception as e:
print(f" FAIL: {e}")
results.append(False)
# Summary
passed = sum(results)
total = len(results)
print(f"Results: {passed}/{total} passed")
if passed == total:
print("ALL TESTS PASSED")
else:
print("SOME TESTS FAILED")
sys.exit(1)
asyncio.run(main())Output |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds 3 fields to
AgentDefinitionthat are present in the TS SDK'sAgentDefinitionSchema(coreSchemas.ts) but missing from the Python SDK:background: bool | None-- Run this agent as a background task (non-blocking, fire-and-forget) when invoked. This is the key addition: it enables parallel subagent execution without spawning multiple SDK sessions.effort: Literal["low", "medium", "high", "max"] | int | None-- Reasoning effort level for the agent. Accepts either a named level or an integer.permissionMode: PermissionMode | None-- Permission mode controlling how tool executions are handled for this agent.All three are optional pass-through fields that the CLI already supports via the
AgentDefinitionSchemain the control protocol. The existingasdict()+ None-filtering serialization in_internal/client.pyhandles them automatically -- no serialization changes needed.permissionModereuses the existingPermissionModetype alias already defined intypes.py.Tests
Added 5 new tests in
TestAgentDefinition:test_background_serializes_correctly-- verifies boolean serializationtest_effort_accepts_named_level-- verifies string effort ("high")test_effort_accepts_integer-- verifies integer effort (32000)test_permission_mode_serializes_as_camelcase-- verifies camelCase keytest_new_fields_omitted_when_none-- verifies all 3 fields absent when unsetAll 44 tests pass.