clawd-code-sdk 0.3.6__tar.gz → 0.3.7__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.
Files changed (70) hide show
  1. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/PKG-INFO +1 -1
  2. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/pyproject.toml +1 -1
  3. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/__init__.py +12 -0
  4. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/agents.py +1 -0
  5. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/base.py +2 -0
  6. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/hooks.py +3 -1
  7. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/input_types.py +27 -0
  8. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/messages.py +15 -24
  9. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/output_types.py +100 -4
  10. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_agents_and_settings.py +2 -2
  11. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_dynamic_control.py +6 -27
  12. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_hook_events.py +1 -1
  13. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_hooks.py +1 -1
  14. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_include_partial_messages.py +15 -12
  15. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_mcp_tools.py +3 -3
  16. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_sdk_mcp_tools.py +4 -0
  17. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_slash_commands.py +1 -1
  18. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_stderr_callback.py +1 -1
  19. clawd_code_sdk-0.3.7/tests/e2e/test_subagent_invocation.py +184 -0
  20. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_tool_permissions.py +6 -7
  21. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/mock_claude_server.py +16 -0
  22. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_client.py +25 -1
  23. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_integration.py +60 -2
  24. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_message_parser.py +0 -55
  25. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_session.py +29 -0
  26. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_streaming_client.py +47 -0
  27. clawd_code_sdk-0.3.6/tests/e2e/test_subagent_invocation.py +0 -112
  28. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/.gitignore +0 -0
  29. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/LICENSE +0 -0
  30. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/README.md +0 -0
  31. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/__init__.py +0 -0
  32. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_bundled/.gitignore +0 -0
  33. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_errors.py +0 -0
  34. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_internal/__init__.py +0 -0
  35. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_internal/message_parser.py +0 -0
  36. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_internal/query.py +0 -0
  37. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_internal/transport/__init__.py +0 -0
  38. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_internal/transport/subprocess_cli.py +0 -0
  39. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/_version.py +0 -0
  40. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/anthropic_types.py +0 -0
  41. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/client.py +0 -0
  42. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/list_sessions.py +0 -0
  43. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/mcp_utils.py +0 -0
  44. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/content_blocks.py +0 -0
  45. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/control.py +0 -0
  46. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/mcp.py +0 -0
  47. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/options.py +0 -0
  48. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/permissions.py +0 -0
  49. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/sandbox.py +0 -0
  50. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/models/server_info.py +0 -0
  51. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/py.typed +0 -0
  52. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/query.py +0 -0
  53. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/session.py +0 -0
  54. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/storage/ARCHITECTURE.md +0 -0
  55. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/storage/__init__.py +0 -0
  56. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/storage/helpers.py +0 -0
  57. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/storage/models.py +0 -0
  58. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/storage/replay.py +0 -0
  59. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/src/clawd_code_sdk/usage.py +0 -0
  60. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/conftest.py +0 -0
  61. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/__init__.py +0 -0
  62. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/e2e/test_structured_output.py +0 -0
  63. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/mcp_server.py +0 -0
  64. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_changelog.py +0 -0
  65. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_errors.py +0 -0
  66. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_image.png +0 -0
  67. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_sdk_mcp_integration.py +0 -0
  68. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_subprocess_buffering.py +0 -0
  69. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_tool_callbacks.py +0 -0
  70. {clawd_code_sdk-0.3.6 → clawd_code_sdk-0.3.7}/tests/test_transport.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawd-code-sdk
3
- Version: 0.3.6
3
+ Version: 0.3.7
4
4
  Summary: Python SDK for Claude Code
5
5
  Project-URL: Documentation, https://github.com/phil65/claude-agent-sdk-python
6
6
  Project-URL: Homepage, https://github.com/phil65/claude-agent-sdk-python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "clawd-code-sdk"
3
- version = "0.3.6"
3
+ version = "0.3.7"
4
4
  description = "Python SDK for Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -90,6 +90,7 @@ from .input_types import (
90
90
  AskUserQuestionOption,
91
91
  BashInput,
92
92
  BashOutputInput,
93
+ EnterPlanModeInput,
93
94
  ExitPlanModeInput,
94
95
  FileEditInput,
95
96
  FileReadInput,
@@ -100,6 +101,7 @@ from .input_types import (
100
101
  ListMcpResourcesInput,
101
102
  NotebookEditInput,
102
103
  ReadMcpResourceInput,
104
+ SkillInput,
103
105
  TodoItem,
104
106
  TodoWriteInput,
105
107
  ToolInput,
@@ -141,11 +143,14 @@ from .output_types import (
141
143
  AskUserQuestionItem,
142
144
  AskUserQuestionOutput,
143
145
  BashOutput,
146
+ BashOutputOutput,
144
147
  BashToolUseResult,
145
148
  ConfigOutput,
146
149
  EditOutput,
147
150
  EditToolUseResult,
151
+ EnterPlanModeOutput,
148
152
  EnterWorktreeOutput,
153
+ KillShellOutput,
149
154
  ExitPlanModeOutput,
150
155
  GitDiff,
151
156
  GlobOutput,
@@ -169,6 +174,7 @@ from .output_types import (
169
174
  ReadPdfOutput,
170
175
  ReadTextOutput,
171
176
  ReadToolUseResult,
177
+ SkillOutput,
172
178
  StructuredPatchHunk,
173
179
  SubscribeMcpResourceOutput,
174
180
  SubscribePollingOutput,
@@ -426,9 +432,11 @@ __all__ = [
426
432
  "WebSearchInput",
427
433
  "TodoItem",
428
434
  "TodoWriteInput",
435
+ "EnterPlanModeInput",
429
436
  "ExitPlanModeInput",
430
437
  "ListMcpResourcesInput",
431
438
  "ReadMcpResourceInput",
439
+ "SkillInput",
432
440
  # output_types (actual tool_use_result wire format)
433
441
  "TOOL_USE_RESULT_TYPES",
434
442
  "AgentAsyncLaunchedOutput",
@@ -442,9 +450,12 @@ __all__ = [
442
450
  "AskUserQuestionItem",
443
451
  "AskUserQuestionOutput",
444
452
  "BashOutput",
453
+ "BashOutputOutput",
445
454
  "ConfigOutput",
446
455
  "EditOutput",
456
+ "EnterPlanModeOutput",
447
457
  "EnterWorktreeOutput",
458
+ "KillShellOutput",
448
459
  "ExitPlanModeOutput",
449
460
  "GitDiff",
450
461
  "GlobOutput",
@@ -467,6 +478,7 @@ __all__ = [
467
478
  "ReadPdfFileInfo",
468
479
  "ReadPdfOutput",
469
480
  "ReadTextOutput",
481
+ "SkillOutput",
470
482
  "StructuredPatchHunk",
471
483
  "SubscribeMcpResourceOutput",
472
484
  "SubscribePollingOutput",
@@ -53,6 +53,7 @@ class AgentDefinition:
53
53
  critical_system_reminder_experimental: str | None = None
54
54
  skills: list[str] | None = None
55
55
  max_turns: int | None = None
56
+ background: bool | None = None
56
57
  # permission_mode: PermissionMode | None = None
57
58
  # hooks: dict[HookEvent, list[HookMatcher]] | None = None
58
59
 
@@ -22,6 +22,8 @@ ModelName = Literal["sonnet", "opus", "haiku"]
22
22
  PermissionBehavior = Literal["allow", "deny", "ask"]
23
23
  ReasoningEffort = Literal["low", "medium", "high", "max"]
24
24
  TaskStatus = Literal["completed", "failed", "stopped"]
25
+ CompactionTrigger = Literal["auto", "manual"]
26
+
25
27
  StopReason = Literal[
26
28
  "end_turn",
27
29
  "max_tokens",
@@ -6,6 +6,8 @@ from collections.abc import Awaitable, Callable
6
6
  from dataclasses import dataclass, field
7
7
  from typing import TYPE_CHECKING, Any, Literal, NotRequired, TypedDict
8
8
 
9
+ from clawd_code_sdk.models.base import CompactionTrigger # noqa: TC001
10
+
9
11
 
10
12
  if TYPE_CHECKING:
11
13
  from collections.abc import Sequence
@@ -101,7 +103,7 @@ class PreCompactHookInput(BaseHookInput):
101
103
  """Input data for PreCompact hook events."""
102
104
 
103
105
  hook_event_name: Literal["PreCompact"]
104
- trigger: Literal["manual", "auto"]
106
+ trigger: CompactionTrigger
105
107
  custom_instructions: str | None
106
108
 
107
109
 
@@ -252,6 +252,29 @@ class EnterWorktreeInput(TypedDict):
252
252
  """Optional name for the worktree. A random name is generated if not provided."""
253
253
 
254
254
 
255
+ @_extra_allow
256
+ class SkillInput(TypedDict):
257
+ """Input for the Skill tool. Invokes a named skill (slash command).
258
+
259
+ Not part of the official SDK ToolInputSchemas as of SDK v0.2.62.
260
+ Shape derived from empirical testing.
261
+ """
262
+
263
+ skill: str
264
+ """The skill name (e.g., "commit", "review", "debug")."""
265
+ args: NotRequired[str]
266
+ """Optional arguments for the skill."""
267
+
268
+
269
+ @_extra_allow
270
+ class EnterPlanModeInput(TypedDict):
271
+ """Input for the EnterPlanMode tool. Enters planning mode.
272
+
273
+ Not part of the official SDK ToolInputSchemas as of SDK v0.2.62.
274
+ Shape derived from empirical testing.
275
+ """
276
+
277
+
255
278
  @_extra_allow
256
279
  class ExitPlanModeInput(TypedDict):
257
280
  """Input for the ExitPlanMode tool. Exits planning mode for user approval."""
@@ -297,6 +320,8 @@ ToolInput = (
297
320
  | ExitPlanModeInput
298
321
  | ListMcpResourcesInput
299
322
  | ReadMcpResourceInput
323
+ | SkillInput
324
+ | EnterPlanModeInput
300
325
  )
301
326
 
302
327
  #: Mapping from tool name to its input type.
@@ -319,4 +344,6 @@ TOOL_INPUT_TYPES: dict[str, type[ToolInput]] = {
319
344
  "ExitPlanMode": ExitPlanModeInput,
320
345
  "ListMcpResources": ListMcpResourcesInput,
321
346
  "ReadMcpResource": ReadMcpResourceInput,
347
+ "Skill": SkillInput,
348
+ "EnterPlanMode": EnterPlanModeInput,
322
349
  }
@@ -23,7 +23,14 @@ from clawd_code_sdk.models.content_blocks import ContentBlock, TextBlock # noqa
23
23
  from clawd_code_sdk.models.mcp import McpConnectionStatus # noqa: TC001
24
24
  from clawd_code_sdk.models.output_types import ToolUseResult # noqa: TC001
25
25
 
26
- from .base import ApiKeySource, PermissionMode, StopReason, TaskStatus, ToolName # noqa: TC001
26
+ from .base import ( # noqa: TC001
27
+ ApiKeySource,
28
+ CompactionTrigger,
29
+ PermissionMode,
30
+ StopReason,
31
+ TaskStatus,
32
+ ToolName,
33
+ )
27
34
 
28
35
 
29
36
  if TYPE_CHECKING:
@@ -178,14 +185,14 @@ class InitSystemMessage(BaseSystemMessage):
178
185
  """System init message with session metadata."""
179
186
 
180
187
  subtype: Literal["init"] = "init"
181
- apiKeySource: ApiKeySource | None = None # noqa: N815
188
+ apiKeySource: ApiKeySource | None # noqa: N815
182
189
  cwd: str
183
190
  tools: list[str]
184
191
  mcp_servers: list[McpServerStatus]
185
192
  model: Model
186
193
  permissionMode: PermissionMode # noqa: N815
187
194
  slash_commands: list[str]
188
- output_style: Literal["default", "json"]
195
+ output_style: Literal["default", "json"] | str
189
196
  claude_code_version: str
190
197
  agents: list[str]
191
198
  skills: list[str]
@@ -213,7 +220,7 @@ class StatusSystemMessage(BaseSystemMessage):
213
220
 
214
221
 
215
222
  class TriggerMetadata(TypedDict):
216
- trigger: Literal["auto", "manual"]
223
+ trigger: CompactionTrigger
217
224
  pre_tokens: int
218
225
 
219
226
 
@@ -338,21 +345,6 @@ class HookResponseSystemMessage(BaseSystemMessage):
338
345
  output: str
339
346
 
340
347
 
341
- class CacheCreation(TypedDict):
342
- ephemeral_1h_input_tokens: int
343
- ephemeral_5m_input_tokens: int
344
-
345
-
346
- class ServerToolUse(TypedDict):
347
- web_search_requests: int
348
- web_fetch_requests: int
349
- service_tier: Literal["standard", "priority"]
350
- cache_creation: CacheCreation
351
- inference_geo: Literal["not_available"] | str
352
- iterations: list[Any]
353
- speed: str
354
-
355
-
356
348
  class ModelUsage(TypedDict):
357
349
  inputTokens: int
358
350
  outputTokens: int
@@ -362,13 +354,12 @@ class ModelUsage(TypedDict):
362
354
  costUSD: float
363
355
  contextWindow: int
364
356
  maxOutputTokens: int
365
- server_tool_use: ServerToolUse
366
357
 
367
358
 
368
359
  class SDKPermissionDenial(TypedDict):
369
360
  tool_name: ToolName | str
370
361
  tool_use_id: str
371
- tool_input: ToolInput
362
+ tool_input: ToolInput | dict[str, Any]
372
363
 
373
364
 
374
365
  class Usage(TypedDict):
@@ -391,9 +382,9 @@ class BaseResultMessage(BaseMessage):
391
382
  num_turns: int
392
383
  total_cost_usd: float
393
384
  usage: Usage
394
- stop_reason: StopReason | None = None
395
- modelUsage: dict[str, ModelUsage] | None = None # noqa: N815
396
- permission_denials: list[SDKPermissionDenial] | None = None
385
+ stop_reason: StopReason | None
386
+ modelUsage: dict[str, ModelUsage] # noqa: N815
387
+ permission_denials: list[SDKPermissionDenial]
397
388
 
398
389
 
399
390
  @dataclass(kw_only=True)
@@ -94,6 +94,8 @@ class AgentAsyncLaunchedOutput(TypedDict):
94
94
 
95
95
  status: Literal["async_launched"]
96
96
  """Indicates the agent was launched in the background."""
97
+ isAsync: Literal[True]
98
+ """Always True for async-launched agents."""
97
99
  agentId: str
98
100
  """The ID of the async agent."""
99
101
  description: str
@@ -165,8 +167,8 @@ class TaskInfo(TypedDict):
165
167
  """Information about a background task."""
166
168
 
167
169
  task_id: str
168
- task_type: str
169
- status: str
170
+ task_type: Literal["local_bash", "agent"]
171
+ status: Literal["running", "completed", "failed"]
170
172
  description: str
171
173
  output: str
172
174
  exitCode: int | None
@@ -175,11 +177,61 @@ class TaskInfo(TypedDict):
175
177
  class TaskOutputResult(TypedDict):
176
178
  """``tool_use_result`` for the TaskOutput tool (background task polling)."""
177
179
 
178
- retrieval_status: str
179
- """Status of the retrieval: ``"not_ready"``, ``"ready"``, etc."""
180
+ retrieval_status: Literal["completed", "timeout", "running"]
181
+ """Status of the retrieval."""
180
182
  task: TaskInfo
181
183
 
182
184
 
185
+ # ---------------------------------------------------------------------------
186
+ # BashOutput (checking background shells — tool name "BashOutput")
187
+ # ---------------------------------------------------------------------------
188
+
189
+
190
+ class BashOutputOutput(TypedDict):
191
+ """``tool_use_result`` for the BashOutput tool (background shell output retrieval).
192
+
193
+ Not part of the official SDK ToolOutputSchemas as of SDK v0.2.62.
194
+ Shape derived from: https://github.com/kzahel/yepanywhere/blob/main/packages/shared/src/claude-sdk-schema/tool/ToolResultSchemas.ts
195
+ """
196
+
197
+ shellId: NotRequired[str]
198
+ """ID of the background shell."""
199
+ command: NotRequired[str]
200
+ """The command that was executed."""
201
+ status: NotRequired[Literal["running", "completed", "failed"]]
202
+ """Current status of the command."""
203
+ exitCode: NotRequired[int | None]
204
+ """Exit code of the command, or None if still running."""
205
+ stdout: NotRequired[str]
206
+ """Standard output."""
207
+ stderr: NotRequired[str]
208
+ """Standard error."""
209
+ stdoutLines: NotRequired[int]
210
+ """Number of lines in stdout."""
211
+ stderrLines: NotRequired[int]
212
+ """Number of lines in stderr."""
213
+ timestamp: NotRequired[str]
214
+ """Timestamp of the output."""
215
+
216
+
217
+ # ---------------------------------------------------------------------------
218
+ # KillShell (kill background shell — tool name "KillBash")
219
+ # ---------------------------------------------------------------------------
220
+
221
+
222
+ class KillShellOutput(TypedDict):
223
+ """``tool_use_result`` for the KillBash tool.
224
+
225
+ Not part of the official SDK ToolOutputSchemas as of SDK v0.2.62.
226
+ Shape derived from: https://github.com/kzahel/yepanywhere/blob/main/packages/shared/src/claude-sdk-schema/tool/ToolResultSchemas.ts
227
+ """
228
+
229
+ message: NotRequired[str]
230
+ """Status message."""
231
+ shell_id: NotRequired[str]
232
+ """ID of the shell that was killed."""
233
+
234
+
183
235
  # ---------------------------------------------------------------------------
184
236
  # TaskStop
185
237
  # ---------------------------------------------------------------------------
@@ -616,6 +668,42 @@ class ConfigOutput(TypedDict):
616
668
  """Error message if the operation failed."""
617
669
 
618
670
 
671
+ # ---------------------------------------------------------------------------
672
+ # Skill
673
+ # ---------------------------------------------------------------------------
674
+
675
+
676
+ class SkillOutput(TypedDict):
677
+ """``tool_use_result`` for the Skill tool.
678
+
679
+ Not part of the official SDK ToolOutputSchemas as of SDK v0.2.62.
680
+ Shape derived from empirical testing.
681
+ """
682
+
683
+ success: bool
684
+ """Whether the skill was invoked successfully."""
685
+ commandName: str
686
+ """The name of the skill that was invoked."""
687
+ allowedTools: list[str]
688
+ """Tools that the skill is allowed to use."""
689
+
690
+
691
+ # ---------------------------------------------------------------------------
692
+ # EnterPlanMode
693
+ # ---------------------------------------------------------------------------
694
+
695
+
696
+ class EnterPlanModeOutput(TypedDict):
697
+ """``tool_use_result`` for the EnterPlanMode tool.
698
+
699
+ Not part of the official SDK ToolOutputSchemas as of SDK v0.2.62.
700
+ Shape derived from empirical testing.
701
+ """
702
+
703
+ message: str
704
+ """A message indicating that plan mode was entered."""
705
+
706
+
619
707
  # ---------------------------------------------------------------------------
620
708
  # EnterWorktree
621
709
  # ---------------------------------------------------------------------------
@@ -639,6 +727,8 @@ class EnterWorktreeOutput(TypedDict):
639
727
  ToolUseResult = (
640
728
  AgentOutput
641
729
  | BashOutput
730
+ | BashOutputOutput
731
+ | KillShellOutput
642
732
  | TaskOutputResult
643
733
  | TaskStopOutput
644
734
  | ExitPlanModeOutput
@@ -659,6 +749,8 @@ ToolUseResult = (
659
749
  | UnsubscribePollingOutput
660
750
  | ConfigOutput
661
751
  | EnterWorktreeOutput
752
+ | SkillOutput
753
+ | EnterPlanModeOutput
662
754
  )
663
755
 
664
756
  #: Backwards-compatible aliases for names previously in ``tool_use_results.py``.
@@ -674,6 +766,8 @@ TodoWriteToolUseResult = TodoWriteOutput
674
766
  TOOL_USE_RESULT_TYPES: dict[str, type[Any]] = {
675
767
  "Task": AgentCompletedOutput,
676
768
  "Bash": BashOutput,
769
+ "BashOutput": BashOutputOutput,
770
+ "KillBash": KillShellOutput,
677
771
  "TaskOutput": TaskOutputResult,
678
772
  "TaskStop": TaskStopOutput,
679
773
  "ExitPlanMode": ExitPlanModeOutput,
@@ -695,4 +789,6 @@ TOOL_USE_RESULT_TYPES: dict[str, type[Any]] = {
695
789
  "UnsubscribePolling": UnsubscribePollingOutput,
696
790
  "Config": ConfigOutput,
697
791
  "EnterWorktree": EnterWorktreeOutput,
792
+ "Skill": SkillOutput,
793
+ "EnterPlanMode": EnterPlanModeOutput,
698
794
  }
@@ -188,7 +188,7 @@ You are a simple test agent. When asked a question, provide a brief, helpful ans
188
188
  f"Missing AssistantMessage - got only: {message_types}. "
189
189
  "This may indicate issue #406 (silent failure with filesystem agents)."
190
190
  )
191
- assert "ResultMessage" in message_types, "Missing ResultMessage"
191
+ assert "ResultSuccessMessage" in message_types, "Missing ResultMessage"
192
192
 
193
193
  # Find the init message and check for the filesystem agent
194
194
  for msg in messages:
@@ -517,4 +517,4 @@ async def test_agent_definition_with_mcp_servers():
517
517
 
518
518
 
519
519
  if __name__ == "__main__":
520
- pytest.main([__file__, "-vv"])
520
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -2,10 +2,7 @@
2
2
 
3
3
  import pytest
4
4
 
5
- from clawd_code_sdk import (
6
- ClaudeAgentOptions,
7
- ClaudeSDKClient,
8
- )
5
+ from clawd_code_sdk import ClaudeAgentOptions, ClaudeSDKClient
9
6
 
10
7
 
11
8
  @pytest.mark.e2e
@@ -13,27 +10,21 @@ from clawd_code_sdk import (
13
10
  async def test_set_permission_mode():
14
11
  """Test that permission mode can be changed dynamically during a session."""
15
12
 
16
- options = ClaudeAgentOptions(
17
- permission_mode="default",
18
- )
13
+ options = ClaudeAgentOptions(permission_mode="default")
19
14
 
20
15
  async with ClaudeSDKClient(options=options) as client:
21
16
  # Change permission mode to acceptEdits
22
17
  await client.set_permission_mode("acceptEdits")
23
-
24
18
  # Make a query that would normally require permission
25
19
  await client.query("What is 2+2? Just respond with the number.")
26
-
27
20
  async for message in client.receive_response():
28
21
  print(f"Got message: {message}")
29
22
  pass # Just consume messages
30
23
 
31
24
  # Change back to default
32
25
  await client.set_permission_mode("default")
33
-
34
26
  # Make another query
35
27
  await client.query("What is 3+3? Just respond with the number.")
36
-
37
28
  async for message in client.receive_response():
38
29
  print(f"Got message: {message}")
39
30
  pass # Just consume messages
@@ -43,31 +34,22 @@ async def test_set_permission_mode():
43
34
  @pytest.mark.asyncio
44
35
  async def test_set_model():
45
36
  """Test that model can be changed dynamically during a session."""
46
-
47
- options = ClaudeAgentOptions()
48
-
49
- async with ClaudeSDKClient(options=options) as client:
37
+ async with ClaudeSDKClient() as client:
50
38
  # Start with default model
51
39
  await client.query("What is 1+1? Just the number.")
52
-
53
40
  async for message in client.receive_response():
54
41
  print(f"Default model response: {message}")
55
42
  pass
56
-
57
43
  # Switch to Haiku model
58
- await client.set_model("claude-3-5-haiku-20241022")
59
-
44
+ await client.set_model("claude-haiku-4-5")
60
45
  await client.query("What is 2+2? Just the number.")
61
-
62
46
  async for message in client.receive_response():
63
47
  print(f"Haiku model response: {message}")
64
48
  pass
65
49
 
66
50
  # Switch back to default (None means default)
67
51
  await client.set_model(None)
68
-
69
52
  await client.query("What is 3+3? Just the number.")
70
-
71
53
  async for message in client.receive_response():
72
54
  print(f"Back to default model: {message}")
73
55
  pass
@@ -78,12 +60,9 @@ async def test_set_model():
78
60
  async def test_interrupt():
79
61
  """Test that interrupt can be sent during a session."""
80
62
 
81
- options = ClaudeAgentOptions()
82
-
83
- async with ClaudeSDKClient(options=options) as client:
63
+ async with ClaudeSDKClient() as client:
84
64
  # Start a query
85
65
  await client.query("Count from 1 to 100 slowly.")
86
-
87
66
  # Send interrupt (may or may not stop the response depending on timing)
88
67
  try:
89
68
  await client.interrupt()
@@ -98,4 +77,4 @@ async def test_interrupt():
98
77
 
99
78
 
100
79
  if __name__ == "__main__":
101
- pytest.main([__file__, "-vv"])
80
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -196,4 +196,4 @@ async def test_multiple_hooks_together():
196
196
 
197
197
 
198
198
  if __name__ == "__main__":
199
- pytest.main([__file__, "-vv"])
199
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -158,4 +158,4 @@ async def test_hook_with_additional_context():
158
158
 
159
159
 
160
160
  if __name__ == "__main__":
161
- pytest.main([__file__, "-vv"])
161
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -6,6 +6,7 @@ including StreamEvent parsing and message interleaving.
6
6
 
7
7
  from typing import Any
8
8
 
9
+ from anthropic.types import RawContentBlockDeltaEvent, ThinkingDelta
9
10
  import pytest
10
11
 
11
12
  from clawd_code_sdk import ClaudeSDKClient
@@ -18,6 +19,7 @@ from clawd_code_sdk.models import (
18
19
  TextBlock,
19
20
  ThinkingBlock,
20
21
  )
22
+ from clawd_code_sdk.models.base import ThinkingConfigEnabled
21
23
 
22
24
 
23
25
  @pytest.mark.e2e
@@ -55,7 +57,7 @@ async def test_include_partial_messages_stream_events():
55
57
  assert len(stream_events) > 0, "No StreamEvent messages received"
56
58
 
57
59
  # Check for expected StreamEvent types
58
- event_types = [event.event.get("type") for event in stream_events]
60
+ event_types = [event.event.type for event in stream_events]
59
61
  assert "message_start" in event_types, "No message_start StreamEvent"
60
62
  assert "content_block_start" in event_types, "No content_block_start StreamEvent"
61
63
  assert "content_block_delta" in event_types, "No content_block_delta StreamEvent"
@@ -91,9 +93,7 @@ async def test_include_partial_messages_thinking_deltas():
91
93
  options = ClaudeAgentOptions(
92
94
  model="claude-sonnet-4-5",
93
95
  max_turns=2,
94
- env={
95
- "MAX_THINKING_TOKENS": "8000",
96
- },
96
+ thinking=ThinkingConfigEnabled(type="enabled", budget_tokens=8000),
97
97
  )
98
98
 
99
99
  thinking_deltas = []
@@ -102,19 +102,22 @@ async def test_include_partial_messages_thinking_deltas():
102
102
  await client.query("Think step by step about what 2 + 2 equals")
103
103
 
104
104
  async for message in client.receive_response():
105
- if isinstance(message, StreamEvent):
106
- event = message.event
107
- if event.get("type") == "content_block_delta":
108
- delta = event.get("delta", {})
109
- if delta.get("type") == "thinking_delta":
110
- thinking_deltas.append(delta.get("thinking", ""))
105
+ print(message)
106
+ if (
107
+ isinstance(message, StreamEvent)
108
+ and isinstance(message.event, RawContentBlockDeltaEvent)
109
+ and isinstance(message.event.delta, ThinkingDelta)
110
+ ):
111
+ thinking_deltas.append(message.event.delta.thinking)
111
112
 
112
113
  # Should have received multiple thinking deltas
113
114
  assert len(thinking_deltas) > 0, "No thinking deltas received"
114
-
115
115
  # Combined thinking should form coherent text
116
116
  combined_thinking = "".join(thinking_deltas)
117
117
  assert len(combined_thinking) > 10, "Thinking content too short"
118
-
119
118
  # Should contain some reasoning about the calculation
120
119
  assert "2" in combined_thinking.lower(), "Thinking doesn't mention the numbers"
120
+
121
+
122
+ if __name__ == "__main__":
123
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -58,7 +58,7 @@ async def test_mcp_image_tool_wire_format():
58
58
  # Verify we got a result
59
59
  result_messages = [m for m in messages if isinstance(m, ResultMessage)]
60
60
  assert result_messages, f"No ResultMessage. Got: {[type(m).__name__ for m in messages]}"
61
- assert not result_messages[0].is_error, f"Query error: {result_messages[0].errors}"
61
+ assert not result_messages[0].is_error
62
62
 
63
63
  # Inspect all content blocks from assistant and user messages
64
64
  all_content_blocks: list[dict[str, Any]] = []
@@ -104,7 +104,7 @@ async def test_mcp_image_tool_wire_format():
104
104
  assert "source" in image_param
105
105
  assert image_param["source"]["type"] == "base64"
106
106
  assert image_param["source"]["media_type"] == "image/png"
107
- assert len(image_param["source"]["data"]) > 0, "Image data should not be empty"
107
+ assert len(image_param["source"]["data"]) > 0, "Image data should not be empty" # pyright: ignore[reportArgumentType]
108
108
 
109
109
 
110
110
  @pytest.mark.e2e
@@ -143,7 +143,7 @@ async def test_mcp_progress_tool_wire_format():
143
143
  # Verify we got a successful result
144
144
  result_messages = [m for m in messages if isinstance(m, ResultMessage)]
145
145
  assert result_messages, f"No ResultMessage. Got: {[type(m).__name__ for m in messages]}"
146
- assert not result_messages[0].is_error, f"Query error: {result_messages[0].errors}"
146
+ assert not result_messages[0].is_error
147
147
 
148
148
  # Collect assistant content blocks to verify tool_use
149
149
  all_content_blocks: list[dict[str, Any]] = []
@@ -127,3 +127,7 @@ async def test_sdk_mcp_without_permissions():
127
127
  pass # Just consume messages
128
128
 
129
129
  assert "echo" not in executions, "SDK MCP tool was executed"
130
+
131
+
132
+ if __name__ == "__main__":
133
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -21,4 +21,4 @@ async def test_compact() -> None:
21
21
 
22
22
 
23
23
  if __name__ == "__main__":
24
- pytest.main(["-v", __file__])
24
+ pytest.main([__file__, "-vv", "-m", "e2e"])
@@ -47,4 +47,4 @@ async def test_stderr_callback_without_debug():
47
47
 
48
48
 
49
49
  if __name__ == "__main__":
50
- pytest.main([__file__, "-vv"])
50
+ pytest.main([__file__, "-vv", "-m", "e2e"])