claude-agent-sdk 0.1.4__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-agent-sdk might be problematic. Click here for more details.
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/.gitignore +1 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/PKG-INFO +1 -1
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/pyproject.toml +1 -1
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/__init__.py +3 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/transport/subprocess_cli.py +64 -2
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_version.py +1 -1
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/types.py +15 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_integration.py +70 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_transport.py +13 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/LICENSE +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/README.md +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_errors.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/__init__.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/client.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/message_parser.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/query.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/transport/__init__.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/client.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/py.typed +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/query.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/conftest.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_changelog.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_client.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_errors.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_message_parser.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_sdk_mcp_integration.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_streaming_client.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_subprocess_buffering.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_tool_callbacks.py +0 -0
- {claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/tests/test_types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-agent-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Python SDK for Claude Code
|
|
5
5
|
Project-URL: Homepage, https://github.com/anthropics/claude-agent-sdk-python
|
|
6
6
|
Project-URL: Documentation, https://docs.anthropic.com/en/docs/claude-code/sdk
|
|
@@ -39,6 +39,7 @@ from .types import (
|
|
|
39
39
|
PreCompactHookInput,
|
|
40
40
|
PreToolUseHookInput,
|
|
41
41
|
ResultMessage,
|
|
42
|
+
SdkPluginConfig,
|
|
42
43
|
SettingSource,
|
|
43
44
|
StopHookInput,
|
|
44
45
|
SubagentStopHookInput,
|
|
@@ -339,6 +340,8 @@ __all__ = [
|
|
|
339
340
|
# Agent support
|
|
340
341
|
"AgentDefinition",
|
|
341
342
|
"SettingSource",
|
|
343
|
+
# Plugin support
|
|
344
|
+
"SdkPluginConfig",
|
|
342
345
|
# MCP Server Support
|
|
343
346
|
"create_sdk_mcp_server",
|
|
344
347
|
"tool",
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
|
+
import platform
|
|
6
7
|
import re
|
|
7
8
|
import shutil
|
|
8
9
|
import sys
|
|
10
|
+
import tempfile
|
|
9
11
|
from collections.abc import AsyncIterable, AsyncIterator
|
|
10
12
|
from contextlib import suppress
|
|
11
13
|
from dataclasses import asdict
|
|
@@ -29,6 +31,11 @@ logger = logging.getLogger(__name__)
|
|
|
29
31
|
_DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 # 1MB buffer limit
|
|
30
32
|
MINIMUM_CLAUDE_CODE_VERSION = "2.0.0"
|
|
31
33
|
|
|
34
|
+
# Platform-specific command line length limits
|
|
35
|
+
# Windows cmd.exe has a limit of 8191 characters, use 8000 for safety
|
|
36
|
+
# Other platforms have much higher limits
|
|
37
|
+
_CMD_LENGTH_LIMIT = 8000 if platform.system() == "Windows" else 100000
|
|
38
|
+
|
|
32
39
|
|
|
33
40
|
class SubprocessCLITransport(Transport):
|
|
34
41
|
"""Subprocess transport using Claude Code CLI."""
|
|
@@ -57,6 +64,7 @@ class SubprocessCLITransport(Transport):
|
|
|
57
64
|
if options.max_buffer_size is not None
|
|
58
65
|
else _DEFAULT_MAX_BUFFER_SIZE
|
|
59
66
|
)
|
|
67
|
+
self._temp_files: list[str] = [] # Track temporary files for cleanup
|
|
60
68
|
|
|
61
69
|
def _find_cli(self) -> str:
|
|
62
70
|
"""Find Claude Code CLI binary."""
|
|
@@ -89,7 +97,7 @@ class SubprocessCLITransport(Transport):
|
|
|
89
97
|
cmd = [self._cli_path, "--output-format", "stream-json", "--verbose"]
|
|
90
98
|
|
|
91
99
|
if self._options.system_prompt is None:
|
|
92
|
-
|
|
100
|
+
cmd.extend(["--system-prompt", ""])
|
|
93
101
|
elif isinstance(self._options.system_prompt, str):
|
|
94
102
|
cmd.extend(["--system-prompt", self._options.system_prompt])
|
|
95
103
|
else:
|
|
@@ -107,6 +115,9 @@ class SubprocessCLITransport(Transport):
|
|
|
107
115
|
if self._options.max_turns:
|
|
108
116
|
cmd.extend(["--max-turns", str(self._options.max_turns)])
|
|
109
117
|
|
|
118
|
+
if self._options.max_budget_usd is not None:
|
|
119
|
+
cmd.extend(["--max-budget-usd", str(self._options.max_budget_usd)])
|
|
120
|
+
|
|
110
121
|
if self._options.disallowed_tools:
|
|
111
122
|
cmd.extend(["--disallowedTools", ",".join(self._options.disallowed_tools)])
|
|
112
123
|
|
|
@@ -173,7 +184,8 @@ class SubprocessCLITransport(Transport):
|
|
|
173
184
|
name: {k: v for k, v in asdict(agent_def).items() if v is not None}
|
|
174
185
|
for name, agent_def in self._options.agents.items()
|
|
175
186
|
}
|
|
176
|
-
|
|
187
|
+
agents_json = json.dumps(agents_dict)
|
|
188
|
+
cmd.extend(["--agents", agents_json])
|
|
177
189
|
|
|
178
190
|
sources_value = (
|
|
179
191
|
",".join(self._options.setting_sources)
|
|
@@ -182,6 +194,14 @@ class SubprocessCLITransport(Transport):
|
|
|
182
194
|
)
|
|
183
195
|
cmd.extend(["--setting-sources", sources_value])
|
|
184
196
|
|
|
197
|
+
# Add plugin directories
|
|
198
|
+
if self._options.plugins:
|
|
199
|
+
for plugin in self._options.plugins:
|
|
200
|
+
if plugin["type"] == "local":
|
|
201
|
+
cmd.extend(["--plugin-dir", plugin["path"]])
|
|
202
|
+
else:
|
|
203
|
+
raise ValueError(f"Unsupported plugin type: {plugin['type']}")
|
|
204
|
+
|
|
185
205
|
# Add extra args for future CLI flags
|
|
186
206
|
for flag, value in self._options.extra_args.items():
|
|
187
207
|
if value is None:
|
|
@@ -199,6 +219,42 @@ class SubprocessCLITransport(Transport):
|
|
|
199
219
|
# String mode: use --print with the prompt
|
|
200
220
|
cmd.extend(["--print", "--", str(self._prompt)])
|
|
201
221
|
|
|
222
|
+
if self._options.max_thinking_tokens is not None:
|
|
223
|
+
cmd.extend(
|
|
224
|
+
["--max-thinking-tokens", str(self._options.max_thinking_tokens)]
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Check if command line is too long (Windows limitation)
|
|
228
|
+
cmd_str = " ".join(cmd)
|
|
229
|
+
if len(cmd_str) > _CMD_LENGTH_LIMIT and self._options.agents:
|
|
230
|
+
# Command is too long - use temp file for agents
|
|
231
|
+
# Find the --agents argument and replace its value with @filepath
|
|
232
|
+
try:
|
|
233
|
+
agents_idx = cmd.index("--agents")
|
|
234
|
+
agents_json_value = cmd[agents_idx + 1]
|
|
235
|
+
|
|
236
|
+
# Create a temporary file
|
|
237
|
+
# ruff: noqa: SIM115
|
|
238
|
+
temp_file = tempfile.NamedTemporaryFile(
|
|
239
|
+
mode="w", suffix=".json", delete=False, encoding="utf-8"
|
|
240
|
+
)
|
|
241
|
+
temp_file.write(agents_json_value)
|
|
242
|
+
temp_file.close()
|
|
243
|
+
|
|
244
|
+
# Track for cleanup
|
|
245
|
+
self._temp_files.append(temp_file.name)
|
|
246
|
+
|
|
247
|
+
# Replace agents JSON with @filepath reference
|
|
248
|
+
cmd[agents_idx + 1] = f"@{temp_file.name}"
|
|
249
|
+
|
|
250
|
+
logger.info(
|
|
251
|
+
f"Command line length ({len(cmd_str)}) exceeds limit ({_CMD_LENGTH_LIMIT}). "
|
|
252
|
+
f"Using temp file for --agents: {temp_file.name}"
|
|
253
|
+
)
|
|
254
|
+
except (ValueError, IndexError) as e:
|
|
255
|
+
# This shouldn't happen, but log it just in case
|
|
256
|
+
logger.warning(f"Failed to optimize command line length: {e}")
|
|
257
|
+
|
|
202
258
|
return cmd
|
|
203
259
|
|
|
204
260
|
async def connect(self) -> None:
|
|
@@ -309,6 +365,12 @@ class SubprocessCLITransport(Transport):
|
|
|
309
365
|
"""Close the transport and clean up resources."""
|
|
310
366
|
self._ready = False
|
|
311
367
|
|
|
368
|
+
# Clean up temporary files first (before early return)
|
|
369
|
+
for temp_file in self._temp_files:
|
|
370
|
+
with suppress(Exception):
|
|
371
|
+
Path(temp_file).unlink(missing_ok=True)
|
|
372
|
+
self._temp_files.clear()
|
|
373
|
+
|
|
312
374
|
if not self._process:
|
|
313
375
|
return
|
|
314
376
|
|
|
@@ -406,6 +406,16 @@ McpServerConfig = (
|
|
|
406
406
|
)
|
|
407
407
|
|
|
408
408
|
|
|
409
|
+
class SdkPluginConfig(TypedDict):
|
|
410
|
+
"""SDK plugin configuration.
|
|
411
|
+
|
|
412
|
+
Currently only local plugins are supported via the 'local' type.
|
|
413
|
+
"""
|
|
414
|
+
|
|
415
|
+
type: Literal["local"]
|
|
416
|
+
path: str
|
|
417
|
+
|
|
418
|
+
|
|
409
419
|
# Content block types
|
|
410
420
|
@dataclass
|
|
411
421
|
class TextBlock:
|
|
@@ -508,6 +518,7 @@ class ClaudeAgentOptions:
|
|
|
508
518
|
continue_conversation: bool = False
|
|
509
519
|
resume: str | None = None
|
|
510
520
|
max_turns: int | None = None
|
|
521
|
+
max_budget_usd: float | None = None
|
|
511
522
|
disallowed_tools: list[str] = field(default_factory=list)
|
|
512
523
|
model: str | None = None
|
|
513
524
|
permission_prompt_tool_name: str | None = None
|
|
@@ -542,6 +553,10 @@ class ClaudeAgentOptions:
|
|
|
542
553
|
agents: dict[str, AgentDefinition] | None = None
|
|
543
554
|
# Setting sources to load (user, project, local)
|
|
544
555
|
setting_sources: list[SettingSource] | None = None
|
|
556
|
+
# Plugin configurations for custom plugins
|
|
557
|
+
plugins: list[SdkPluginConfig] = field(default_factory=list)
|
|
558
|
+
# Max tokens for thinking blocks
|
|
559
|
+
max_thinking_tokens: int | None = None
|
|
545
560
|
|
|
546
561
|
|
|
547
562
|
# SDK Control Protocol
|
|
@@ -212,3 +212,73 @@ class TestIntegration:
|
|
|
212
212
|
assert call_kwargs["options"].continue_conversation is True
|
|
213
213
|
|
|
214
214
|
anyio.run(_test)
|
|
215
|
+
|
|
216
|
+
def test_max_budget_usd_option(self):
|
|
217
|
+
"""Test query with max_budget_usd option."""
|
|
218
|
+
|
|
219
|
+
async def _test():
|
|
220
|
+
with patch(
|
|
221
|
+
"claude_agent_sdk._internal.client.SubprocessCLITransport"
|
|
222
|
+
) as mock_transport_class:
|
|
223
|
+
mock_transport = AsyncMock()
|
|
224
|
+
mock_transport_class.return_value = mock_transport
|
|
225
|
+
|
|
226
|
+
# Mock the message stream that exceeds budget
|
|
227
|
+
async def mock_receive():
|
|
228
|
+
yield {
|
|
229
|
+
"type": "assistant",
|
|
230
|
+
"message": {
|
|
231
|
+
"role": "assistant",
|
|
232
|
+
"content": [
|
|
233
|
+
{"type": "text", "text": "Starting to read..."}
|
|
234
|
+
],
|
|
235
|
+
"model": "claude-opus-4-1-20250805",
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
yield {
|
|
239
|
+
"type": "result",
|
|
240
|
+
"subtype": "error_max_budget_usd",
|
|
241
|
+
"duration_ms": 500,
|
|
242
|
+
"duration_api_ms": 400,
|
|
243
|
+
"is_error": False,
|
|
244
|
+
"num_turns": 1,
|
|
245
|
+
"session_id": "test-session-budget",
|
|
246
|
+
"total_cost_usd": 0.0002,
|
|
247
|
+
"usage": {
|
|
248
|
+
"input_tokens": 100,
|
|
249
|
+
"output_tokens": 50,
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
mock_transport.read_messages = mock_receive
|
|
254
|
+
mock_transport.connect = AsyncMock()
|
|
255
|
+
mock_transport.close = AsyncMock()
|
|
256
|
+
mock_transport.end_input = AsyncMock()
|
|
257
|
+
mock_transport.write = AsyncMock()
|
|
258
|
+
mock_transport.is_ready = Mock(return_value=True)
|
|
259
|
+
|
|
260
|
+
# Run query with very small budget
|
|
261
|
+
messages = []
|
|
262
|
+
async for msg in query(
|
|
263
|
+
prompt="Read the readme",
|
|
264
|
+
options=ClaudeAgentOptions(max_budget_usd=0.0001),
|
|
265
|
+
):
|
|
266
|
+
messages.append(msg)
|
|
267
|
+
|
|
268
|
+
# Verify results
|
|
269
|
+
assert len(messages) == 2
|
|
270
|
+
|
|
271
|
+
# Check result message
|
|
272
|
+
assert isinstance(messages[1], ResultMessage)
|
|
273
|
+
assert messages[1].subtype == "error_max_budget_usd"
|
|
274
|
+
assert messages[1].is_error is False
|
|
275
|
+
assert messages[1].total_cost_usd == 0.0002
|
|
276
|
+
assert messages[1].total_cost_usd is not None
|
|
277
|
+
assert messages[1].total_cost_usd > 0
|
|
278
|
+
|
|
279
|
+
# Verify transport was created with max_budget_usd option
|
|
280
|
+
mock_transport_class.assert_called_once()
|
|
281
|
+
call_kwargs = mock_transport_class.call_args.kwargs
|
|
282
|
+
assert call_kwargs["options"].max_budget_usd == 0.0001
|
|
283
|
+
|
|
284
|
+
anyio.run(_test)
|
|
@@ -46,6 +46,8 @@ class TestSubprocessCLITransport:
|
|
|
46
46
|
assert "stream-json" in cmd
|
|
47
47
|
assert "--print" in cmd
|
|
48
48
|
assert "Hello" in cmd
|
|
49
|
+
assert "--system-prompt" in cmd
|
|
50
|
+
assert cmd[cmd.index("--system-prompt") + 1] == ""
|
|
49
51
|
|
|
50
52
|
def test_cli_path_accepts_pathlib_path(self):
|
|
51
53
|
"""Test that cli_path accepts pathlib.Path objects."""
|
|
@@ -129,6 +131,17 @@ class TestSubprocessCLITransport:
|
|
|
129
131
|
assert "--max-turns" in cmd
|
|
130
132
|
assert "5" in cmd
|
|
131
133
|
|
|
134
|
+
def test_build_command_with_max_thinking_tokens(self):
|
|
135
|
+
"""Test building CLI command with max_thinking_tokens option."""
|
|
136
|
+
transport = SubprocessCLITransport(
|
|
137
|
+
prompt="test",
|
|
138
|
+
options=make_options(max_thinking_tokens=5000),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
cmd = transport._build_command()
|
|
142
|
+
assert "--max-thinking-tokens" in cmd
|
|
143
|
+
assert "5000" in cmd
|
|
144
|
+
|
|
132
145
|
def test_build_command_with_add_dirs(self):
|
|
133
146
|
"""Test building CLI command with add_dirs option."""
|
|
134
147
|
from pathlib import Path
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{claude_agent_sdk-0.1.4 → claude_agent_sdk-0.1.6}/src/claude_agent_sdk/_internal/message_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|