claude-agent-sdk 0.6.3 → 0.7.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +43 -0
- data/README.md +46 -6
- data/lib/claude_agent_sdk/message_parser.rb +5 -2
- data/lib/claude_agent_sdk/query.rb +19 -2
- data/lib/claude_agent_sdk/sdk_mcp_server.rb +6 -3
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +51 -58
- data/lib/claude_agent_sdk/types.rb +51 -11
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +48 -8
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: de5807b36fd822ee89e4a793a536b4ba8804c168259d17cc1a03974425f128ea
|
|
4
|
+
data.tar.gz: c40d5dad151728b8b2e131ad96da616bd03b4972f6d5807d5375ea24d499ceb6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f34dc590db10f0f4ebe1360a5318617fd0b7c6299fcf83d45438d1bb1a9a106734e06268f7d4361bbdf575d660afa63b97586c56235fa9d617a6b891a0e1e158
|
|
7
|
+
data.tar.gz: e6548661fbcd10c96b5b64b4efc6676d5ad47f28435a23e4b8045a590df107bedea2ee8d089a230c32aaa84dafdbb0239bbeffb1b7b7aba255196d2ded16903e
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,49 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-02-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
#### Thinking Configuration
|
|
13
|
+
- `ThinkingConfigAdaptive`, `ThinkingConfigEnabled`, `ThinkingConfigDisabled` classes for structured thinking control
|
|
14
|
+
- `thinking` option on `ClaudeAgentOptions` — takes precedence over deprecated `max_thinking_tokens`
|
|
15
|
+
- `ThinkingConfigAdaptive` → 32,000 token default budget
|
|
16
|
+
- `ThinkingConfigEnabled(budget_tokens:)` → explicit budget
|
|
17
|
+
- `ThinkingConfigDisabled` → 0 tokens (thinking off)
|
|
18
|
+
- `effort` option on `ClaudeAgentOptions` — maps to `--effort` CLI flag (`'low'`, `'medium'`, `'high'`)
|
|
19
|
+
|
|
20
|
+
#### Tool Annotations
|
|
21
|
+
- `annotations` attribute on `SdkMcpTool` for MCP tool annotations (e.g., `readOnlyHint`, `title`)
|
|
22
|
+
- `annotations:` keyword on `ClaudeAgentSDK.create_tool`
|
|
23
|
+
- Annotations included in `SdkMcpServer#list_tools` responses
|
|
24
|
+
|
|
25
|
+
#### Hook Enhancements
|
|
26
|
+
- `tool_use_id` attribute on `PreToolUseHookInput` and `PostToolUseHookInput`
|
|
27
|
+
- `additional_context` attribute on `PreToolUseHookSpecificOutput`
|
|
28
|
+
|
|
29
|
+
#### Message Enhancements
|
|
30
|
+
- `tool_use_result` attribute on `UserMessage` for tool response data
|
|
31
|
+
- `MessageParser` populates `tool_use_result` from CLI output
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
#### Architecture: Always Streaming Mode (BREAKING for internal API)
|
|
36
|
+
- **`SubprocessCLITransport`** now always uses `--input-format stream-json` — removed `--print` mode and `--agents` CLI flag
|
|
37
|
+
- **`SubprocessCLITransport.new`** still accepts `(prompt, options)` for compatibility but ignores the prompt argument (always uses streaming mode)
|
|
38
|
+
- **`query()`** now uses the full control protocol internally (Query handler + initialize handshake), matching the Python SDK
|
|
39
|
+
- **Agents** are sent via the `initialize` control request over stdin instead of CLI `--agents` flag, avoiding OS ARG_MAX limits
|
|
40
|
+
- **`query()`** now supports SDK MCP servers and `can_use_tool` callbacks (previously Client-only)
|
|
41
|
+
|
|
42
|
+
#### Empty System Prompt
|
|
43
|
+
- When `system_prompt` is `nil`, passes `--system-prompt ""` to CLI for predictable behavior without the default Claude Code system prompt
|
|
44
|
+
|
|
45
|
+
## [0.6.3] - 2026-02-18
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
- **ProcessError stderr:** Real stderr output is now included in `ProcessError` exceptions (was always "No stderr output captured")
|
|
49
|
+
- **Rate limit events:** Added `RateLimitEvent` type and `rate_limit_event` message parsing support
|
|
50
|
+
|
|
8
51
|
## [0.6.2] - 2026-02-17
|
|
9
52
|
|
|
10
53
|
### Fixed
|
data/README.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- [Hooks](#hooks)
|
|
17
17
|
- [Permission Callbacks](#permission-callbacks)
|
|
18
18
|
- [Structured Output](#structured-output)
|
|
19
|
+
- [Thinking Configuration](#thinking-configuration)
|
|
19
20
|
- [Budget Control](#budget-control)
|
|
20
21
|
- [Fallback Model](#fallback-model)
|
|
21
22
|
- [Beta Features](#beta-features)
|
|
@@ -38,7 +39,7 @@ Add this line to your application's Gemfile:
|
|
|
38
39
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
39
40
|
|
|
40
41
|
# Or use a stable version from RubyGems
|
|
41
|
-
gem 'claude-agent-sdk', '~> 0.
|
|
42
|
+
gem 'claude-agent-sdk', '~> 0.7.0'
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
And then execute:
|
|
@@ -249,8 +250,11 @@ Custom tools are implemented as in-process MCP servers that run directly within
|
|
|
249
250
|
require 'claude_agent_sdk'
|
|
250
251
|
require 'async'
|
|
251
252
|
|
|
252
|
-
# Define a tool using create_tool
|
|
253
|
-
greet_tool = ClaudeAgentSDK.create_tool(
|
|
253
|
+
# Define a tool using create_tool (with optional annotations)
|
|
254
|
+
greet_tool = ClaudeAgentSDK.create_tool(
|
|
255
|
+
'greet', 'Greet a user', { name: :string },
|
|
256
|
+
annotations: { title: 'Greeter', readOnlyHint: true }
|
|
257
|
+
) do |args|
|
|
254
258
|
{ content: [{ type: 'text', text: "Hello, #{args[:name]}!" }] }
|
|
255
259
|
end
|
|
256
260
|
|
|
@@ -392,8 +396,8 @@ A **hook** is a Ruby proc/lambda that the Claude Code *application* (*not* Claud
|
|
|
392
396
|
|
|
393
397
|
All hook input objects include common fields like `session_id`, `transcript_path`, `cwd`, and `permission_mode`.
|
|
394
398
|
|
|
395
|
-
- `PreToolUse` → `PreToolUseHookInput` (`tool_name`, `tool_input`)
|
|
396
|
-
- `PostToolUse` → `PostToolUseHookInput` (`tool_name`, `tool_input`, `tool_response`)
|
|
399
|
+
- `PreToolUse` → `PreToolUseHookInput` (`tool_name`, `tool_input`, `tool_use_id`)
|
|
400
|
+
- `PostToolUse` → `PostToolUseHookInput` (`tool_name`, `tool_input`, `tool_response`, `tool_use_id`)
|
|
397
401
|
- `PostToolUseFailure` → `PostToolUseFailureHookInput` (`tool_name`, `tool_input`, `tool_use_id`, `error`, `is_interrupt`)
|
|
398
402
|
- `UserPromptSubmit` → `UserPromptSubmitHookInput` (`prompt`)
|
|
399
403
|
- `Stop` → `StopHookInput` (`stop_hook_active`)
|
|
@@ -566,6 +570,37 @@ end
|
|
|
566
570
|
|
|
567
571
|
For complete examples, see [examples/structured_output_example.rb](examples/structured_output_example.rb).
|
|
568
572
|
|
|
573
|
+
## Thinking Configuration
|
|
574
|
+
|
|
575
|
+
Control extended thinking behavior with typed configuration objects. The `thinking` option takes precedence over the deprecated `max_thinking_tokens`.
|
|
576
|
+
|
|
577
|
+
```ruby
|
|
578
|
+
# Adaptive thinking — uses a default budget of 32,000 tokens
|
|
579
|
+
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
580
|
+
thinking: ClaudeAgentSDK::ThinkingConfigAdaptive.new
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
# Enabled thinking with custom budget
|
|
584
|
+
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
585
|
+
thinking: ClaudeAgentSDK::ThinkingConfigEnabled.new(budget_tokens: 50_000)
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
# Explicitly disabled thinking
|
|
589
|
+
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
590
|
+
thinking: ClaudeAgentSDK::ThinkingConfigDisabled.new
|
|
591
|
+
)
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
Use the `effort` option to control the model's effort level:
|
|
595
|
+
|
|
596
|
+
```ruby
|
|
597
|
+
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
598
|
+
effort: 'high' # 'low', 'medium', or 'high'
|
|
599
|
+
)
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
> **Note:** When `system_prompt` is `nil` (the default), the SDK passes `--system-prompt ""` to the CLI, which suppresses the default Claude Code system prompt. To use the default system prompt, use a `SystemPromptPreset`.
|
|
603
|
+
|
|
569
604
|
## Budget Control
|
|
570
605
|
|
|
571
606
|
Use `max_budget_usd` to set a spending cap for your queries:
|
|
@@ -891,7 +926,8 @@ User input message.
|
|
|
891
926
|
class UserMessage
|
|
892
927
|
attr_accessor :content, # String | Array<ContentBlock>
|
|
893
928
|
:uuid, # String | nil - Unique ID for rewind support
|
|
894
|
-
:parent_tool_use_id # String | nil
|
|
929
|
+
:parent_tool_use_id, # String | nil
|
|
930
|
+
:tool_use_result # Hash | nil - Tool result data when message is a tool response
|
|
895
931
|
end
|
|
896
932
|
```
|
|
897
933
|
|
|
@@ -1036,6 +1072,10 @@ end
|
|
|
1036
1072
|
| `PermissionResultAllow` | Permission callback result to allow tool use |
|
|
1037
1073
|
| `PermissionResultDeny` | Permission callback result to deny tool use |
|
|
1038
1074
|
| `AgentDefinition` | Agent definition with description, prompt, tools, model |
|
|
1075
|
+
| `ThinkingConfigAdaptive` | Adaptive thinking mode (32,000 token default budget) |
|
|
1076
|
+
| `ThinkingConfigEnabled` | Enabled thinking with explicit `budget_tokens` |
|
|
1077
|
+
| `ThinkingConfigDisabled` | Disabled thinking (0 tokens) |
|
|
1078
|
+
| `SdkMcpTool` | SDK MCP tool definition with name, description, input_schema, handler, annotations |
|
|
1039
1079
|
| `McpStdioServerConfig` | MCP server config for stdio transport |
|
|
1040
1080
|
| `McpSSEServerConfig` | MCP server config for SSE transport |
|
|
1041
1081
|
| `McpHttpServerConfig` | MCP server config for HTTP transport |
|
|
@@ -35,6 +35,7 @@ module ClaudeAgentSDK
|
|
|
35
35
|
def self.parse_user_message(data)
|
|
36
36
|
parent_tool_use_id = data[:parent_tool_use_id]
|
|
37
37
|
uuid = data[:uuid] # UUID for rewind support
|
|
38
|
+
tool_use_result = data[:tool_use_result]
|
|
38
39
|
message_data = data[:message]
|
|
39
40
|
raise MessageParseError.new("Missing message field in user message", data: data) unless message_data
|
|
40
41
|
|
|
@@ -43,9 +44,11 @@ module ClaudeAgentSDK
|
|
|
43
44
|
|
|
44
45
|
if content.is_a?(Array)
|
|
45
46
|
content_blocks = content.map { |block| parse_content_block(block) }
|
|
46
|
-
UserMessage.new(content: content_blocks, uuid: uuid, parent_tool_use_id: parent_tool_use_id
|
|
47
|
+
UserMessage.new(content: content_blocks, uuid: uuid, parent_tool_use_id: parent_tool_use_id,
|
|
48
|
+
tool_use_result: tool_use_result)
|
|
47
49
|
else
|
|
48
|
-
UserMessage.new(content: content, uuid: uuid, parent_tool_use_id: parent_tool_use_id
|
|
50
|
+
UserMessage.new(content: content, uuid: uuid, parent_tool_use_id: parent_tool_use_id,
|
|
51
|
+
tool_use_result: tool_use_result)
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
54
|
|
|
@@ -23,12 +23,13 @@ module ClaudeAgentSDK
|
|
|
23
23
|
CONTROL_REQUEST_TIMEOUT_ENV_VAR = 'CLAUDE_AGENT_SDK_CONTROL_REQUEST_TIMEOUT_SECONDS'
|
|
24
24
|
DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS = 1200.0
|
|
25
25
|
|
|
26
|
-
def initialize(transport:, is_streaming_mode:, can_use_tool: nil, hooks: nil, sdk_mcp_servers: nil)
|
|
26
|
+
def initialize(transport:, is_streaming_mode:, can_use_tool: nil, hooks: nil, sdk_mcp_servers: nil, agents: nil)
|
|
27
27
|
@transport = transport
|
|
28
28
|
@is_streaming_mode = is_streaming_mode
|
|
29
29
|
@can_use_tool = can_use_tool
|
|
30
30
|
@hooks = hooks || {}
|
|
31
31
|
@sdk_mcp_servers = sdk_mcp_servers || {}
|
|
32
|
+
@agents = agents
|
|
32
33
|
|
|
33
34
|
# Control protocol state
|
|
34
35
|
@pending_control_responses = {}
|
|
@@ -76,10 +77,24 @@ module ClaudeAgentSDK
|
|
|
76
77
|
end
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
# Build agents dict for initialization
|
|
81
|
+
agents_dict = nil
|
|
82
|
+
if @agents
|
|
83
|
+
agents_dict = @agents.transform_values do |agent_def|
|
|
84
|
+
{
|
|
85
|
+
description: agent_def.description,
|
|
86
|
+
prompt: agent_def.prompt,
|
|
87
|
+
tools: agent_def.tools,
|
|
88
|
+
model: agent_def.model
|
|
89
|
+
}.compact
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
79
93
|
# Send initialize request
|
|
80
94
|
request = {
|
|
81
95
|
subtype: 'initialize',
|
|
82
|
-
hooks: hooks_config.empty? ? nil : hooks_config
|
|
96
|
+
hooks: hooks_config.empty? ? nil : hooks_config,
|
|
97
|
+
agents: agents_dict
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
response = send_control_request(request)
|
|
@@ -306,6 +321,7 @@ module ClaudeAgentSDK
|
|
|
306
321
|
PreToolUseHookInput.new(
|
|
307
322
|
tool_name: fetch.call(:tool_name),
|
|
308
323
|
tool_input: fetch.call(:tool_input),
|
|
324
|
+
tool_use_id: fetch.call(:tool_use_id),
|
|
309
325
|
**base_args
|
|
310
326
|
)
|
|
311
327
|
when 'PostToolUse'
|
|
@@ -313,6 +329,7 @@ module ClaudeAgentSDK
|
|
|
313
329
|
tool_name: fetch.call(:tool_name),
|
|
314
330
|
tool_input: fetch.call(:tool_input),
|
|
315
331
|
tool_response: fetch.call(:tool_response),
|
|
332
|
+
tool_use_id: fetch.call(:tool_use_id),
|
|
316
333
|
**base_args
|
|
317
334
|
)
|
|
318
335
|
when 'PostToolUseFailure'
|
|
@@ -51,11 +51,13 @@ module ClaudeAgentSDK
|
|
|
51
51
|
# @return [Array<Hash>] Array of tool definitions
|
|
52
52
|
def list_tools
|
|
53
53
|
@tools.map do |tool|
|
|
54
|
-
{
|
|
54
|
+
entry = {
|
|
55
55
|
name: tool.name,
|
|
56
56
|
description: tool.description,
|
|
57
57
|
inputSchema: convert_input_schema(tool.input_schema)
|
|
58
58
|
}
|
|
59
|
+
entry[:annotations] = tool.annotations if tool.annotations
|
|
60
|
+
entry
|
|
59
61
|
end
|
|
60
62
|
end
|
|
61
63
|
|
|
@@ -387,14 +389,15 @@ module ClaudeAgentSDK
|
|
|
387
389
|
# { content: [{ type: 'text', text: "Result: #{result}" }] }
|
|
388
390
|
# end
|
|
389
391
|
# end
|
|
390
|
-
def self.create_tool(name, description, input_schema, &handler)
|
|
392
|
+
def self.create_tool(name, description, input_schema, annotations: nil, &handler)
|
|
391
393
|
raise ArgumentError, 'Block required for tool handler' unless handler
|
|
392
394
|
|
|
393
395
|
SdkMcpTool.new(
|
|
394
396
|
name: name,
|
|
395
397
|
description: description,
|
|
396
398
|
input_schema: input_schema,
|
|
397
|
-
handler: handler
|
|
399
|
+
handler: handler,
|
|
400
|
+
annotations: annotations
|
|
398
401
|
)
|
|
399
402
|
end
|
|
400
403
|
|
|
@@ -13,15 +13,9 @@ module ClaudeAgentSDK
|
|
|
13
13
|
DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 # 1MB buffer limit
|
|
14
14
|
MINIMUM_CLAUDE_CODE_VERSION = '2.0.0'
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
PROMPT_STDIN_THRESHOLD = 200 * 1024 # 200KB
|
|
20
|
-
|
|
21
|
-
def initialize(prompt, options)
|
|
22
|
-
@prompt = prompt
|
|
23
|
-
@is_streaming = !prompt.is_a?(String)
|
|
24
|
-
@options = options
|
|
16
|
+
def initialize(options_or_prompt = nil, options = nil)
|
|
17
|
+
# Support both new single-arg form and legacy two-arg form
|
|
18
|
+
@options = options.nil? ? options_or_prompt : options
|
|
25
19
|
@cli_path = options.cli_path || find_cli
|
|
26
20
|
@cwd = options.cwd
|
|
27
21
|
@process = nil
|
|
@@ -34,7 +28,6 @@ module ClaudeAgentSDK
|
|
|
34
28
|
@stderr_task = nil
|
|
35
29
|
@recent_stderr = []
|
|
36
30
|
@recent_stderr_mutex = Mutex.new
|
|
37
|
-
@pipe_prompt_via_stdin = false
|
|
38
31
|
end
|
|
39
32
|
|
|
40
33
|
def find_cli
|
|
@@ -76,20 +69,21 @@ module ClaudeAgentSDK
|
|
|
76
69
|
cmd = [@cli_path, '--output-format', 'stream-json', '--verbose']
|
|
77
70
|
|
|
78
71
|
# System prompt handling
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
72
|
+
# When nil, pass empty string to ensure predictable behavior without default Claude Code system prompt
|
|
73
|
+
if @options.system_prompt.nil?
|
|
74
|
+
cmd.concat(['--system-prompt', ''])
|
|
75
|
+
elsif @options.system_prompt.is_a?(String)
|
|
76
|
+
cmd.concat(['--system-prompt', @options.system_prompt])
|
|
77
|
+
elsif @options.system_prompt.is_a?(SystemPromptPreset)
|
|
78
|
+
cmd.concat(['--system-prompt-preset', @options.system_prompt.preset]) if @options.system_prompt.preset
|
|
79
|
+
cmd.concat(['--append-system-prompt', @options.system_prompt.append]) if @options.system_prompt.append
|
|
80
|
+
elsif @options.system_prompt.is_a?(Hash)
|
|
81
|
+
prompt_type = @options.system_prompt[:type] || @options.system_prompt['type']
|
|
82
|
+
if prompt_type == 'preset'
|
|
83
|
+
preset = @options.system_prompt[:preset] || @options.system_prompt['preset']
|
|
84
|
+
append = @options.system_prompt[:append] || @options.system_prompt['append']
|
|
85
|
+
cmd.concat(['--system-prompt-preset', preset]) if preset
|
|
86
|
+
cmd.concat(['--append-system-prompt', append]) if append
|
|
93
87
|
end
|
|
94
88
|
end
|
|
95
89
|
|
|
@@ -147,7 +141,13 @@ module ClaudeAgentSDK
|
|
|
147
141
|
|
|
148
142
|
# Budget limit option
|
|
149
143
|
cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
|
|
150
|
-
|
|
144
|
+
|
|
145
|
+
# Thinking configuration (takes precedence over deprecated max_thinking_tokens)
|
|
146
|
+
thinking_tokens = resolve_thinking_tokens
|
|
147
|
+
cmd.concat(['--max-thinking-tokens', thinking_tokens.to_s]) unless thinking_tokens.nil?
|
|
148
|
+
|
|
149
|
+
# Effort level
|
|
150
|
+
cmd.concat(['--effort', @options.effort.to_s]) if @options.effort
|
|
151
151
|
|
|
152
152
|
# Betas option for enabling experimental features
|
|
153
153
|
if @options.betas && !@options.betas.empty?
|
|
@@ -216,18 +216,8 @@ module ClaudeAgentSDK
|
|
|
216
216
|
cmd << '--include-partial-messages' if @options.include_partial_messages
|
|
217
217
|
cmd << '--fork-session' if @options.fork_session
|
|
218
218
|
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
agents_dict = @options.agents.transform_values do |agent_def|
|
|
222
|
-
{
|
|
223
|
-
description: agent_def.description,
|
|
224
|
-
prompt: agent_def.prompt,
|
|
225
|
-
tools: agent_def.tools,
|
|
226
|
-
model: agent_def.model
|
|
227
|
-
}.compact
|
|
228
|
-
end
|
|
229
|
-
cmd.concat(['--agents', JSON.generate(agents_dict)])
|
|
230
|
-
end
|
|
219
|
+
# Note: agents are now sent via the initialize control request (not CLI args)
|
|
220
|
+
# to avoid OS ARG_MAX limits with large agent configurations.
|
|
231
221
|
|
|
232
222
|
# Plugins
|
|
233
223
|
if @options.plugins && !@options.plugins.empty?
|
|
@@ -250,17 +240,10 @@ module ClaudeAgentSDK
|
|
|
250
240
|
end
|
|
251
241
|
end
|
|
252
242
|
|
|
253
|
-
#
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
# Large prompts are piped via stdin to avoid OS argument size limits.
|
|
258
|
-
# Claude CLI reads from stdin when --print is used without a trailing argument.
|
|
259
|
-
cmd << '--print'
|
|
260
|
-
@pipe_prompt_via_stdin = true
|
|
261
|
-
else
|
|
262
|
-
cmd.concat(['--print', '--', @prompt.to_s])
|
|
263
|
-
end
|
|
243
|
+
# Always use streaming mode for bidirectional control protocol.
|
|
244
|
+
# Prompts and agents are sent via stdin (initialize + user messages),
|
|
245
|
+
# which avoids OS ARG_MAX limits for large prompts and agent configurations.
|
|
246
|
+
cmd.concat(['--input-format', 'stream-json'])
|
|
264
247
|
|
|
265
248
|
cmd
|
|
266
249
|
end
|
|
@@ -313,16 +296,7 @@ module ClaudeAgentSDK
|
|
|
313
296
|
end
|
|
314
297
|
end
|
|
315
298
|
|
|
316
|
-
#
|
|
317
|
-
# This avoids Errno::E2BIG when the prompt exceeds ARG_MAX.
|
|
318
|
-
if @pipe_prompt_via_stdin && @stdin
|
|
319
|
-
@stdin.write(@prompt.to_s)
|
|
320
|
-
end
|
|
321
|
-
|
|
322
|
-
# Close stdin for non-streaming mode
|
|
323
|
-
@stdin.close unless @is_streaming
|
|
324
|
-
@stdin = nil unless @is_streaming
|
|
325
|
-
|
|
299
|
+
# Always keep stdin open — streaming mode uses it for the control protocol
|
|
326
300
|
@ready = true
|
|
327
301
|
rescue Errno::ENOENT => e
|
|
328
302
|
# Check if error is from cwd or CLI
|
|
@@ -575,5 +549,24 @@ module ClaudeAgentSDK
|
|
|
575
549
|
def ready?
|
|
576
550
|
@ready
|
|
577
551
|
end
|
|
552
|
+
|
|
553
|
+
DEFAULT_ADAPTIVE_THINKING_TOKENS = 32_000
|
|
554
|
+
|
|
555
|
+
private
|
|
556
|
+
|
|
557
|
+
def resolve_thinking_tokens
|
|
558
|
+
if @options.thinking
|
|
559
|
+
case @options.thinking
|
|
560
|
+
when ThinkingConfigAdaptive
|
|
561
|
+
DEFAULT_ADAPTIVE_THINKING_TOKENS
|
|
562
|
+
when ThinkingConfigEnabled
|
|
563
|
+
@options.thinking.budget_tokens
|
|
564
|
+
when ThinkingConfigDisabled
|
|
565
|
+
0
|
|
566
|
+
end
|
|
567
|
+
elsif @options.max_thinking_tokens
|
|
568
|
+
@options.max_thinking_tokens
|
|
569
|
+
end
|
|
570
|
+
end
|
|
578
571
|
end
|
|
579
572
|
end
|
|
@@ -81,12 +81,13 @@ module ClaudeAgentSDK
|
|
|
81
81
|
|
|
82
82
|
# User message
|
|
83
83
|
class UserMessage
|
|
84
|
-
attr_accessor :content, :uuid, :parent_tool_use_id
|
|
84
|
+
attr_accessor :content, :uuid, :parent_tool_use_id, :tool_use_result
|
|
85
85
|
|
|
86
|
-
def initialize(content:, uuid: nil, parent_tool_use_id: nil)
|
|
86
|
+
def initialize(content:, uuid: nil, parent_tool_use_id: nil, tool_use_result: nil)
|
|
87
87
|
@content = content
|
|
88
88
|
@uuid = uuid # Unique identifier for rewind support
|
|
89
89
|
@parent_tool_use_id = parent_tool_use_id
|
|
90
|
+
@tool_use_result = tool_use_result # Tool result data when message is a tool response
|
|
90
91
|
end
|
|
91
92
|
end
|
|
92
93
|
|
|
@@ -153,6 +154,36 @@ module ClaudeAgentSDK
|
|
|
153
154
|
end
|
|
154
155
|
end
|
|
155
156
|
|
|
157
|
+
# Thinking configuration types
|
|
158
|
+
|
|
159
|
+
# Adaptive thinking: uses a default budget of 32000 tokens
|
|
160
|
+
class ThinkingConfigAdaptive
|
|
161
|
+
attr_accessor :type
|
|
162
|
+
|
|
163
|
+
def initialize
|
|
164
|
+
@type = 'adaptive'
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Enabled thinking: uses a user-specified budget
|
|
169
|
+
class ThinkingConfigEnabled
|
|
170
|
+
attr_accessor :type, :budget_tokens
|
|
171
|
+
|
|
172
|
+
def initialize(budget_tokens:)
|
|
173
|
+
@type = 'enabled'
|
|
174
|
+
@budget_tokens = budget_tokens
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Disabled thinking: sets thinking tokens to 0
|
|
179
|
+
class ThinkingConfigDisabled
|
|
180
|
+
attr_accessor :type
|
|
181
|
+
|
|
182
|
+
def initialize
|
|
183
|
+
@type = 'disabled'
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
156
187
|
# Agent definition configuration
|
|
157
188
|
class AgentDefinition
|
|
158
189
|
attr_accessor :description, :prompt, :tools, :model
|
|
@@ -278,26 +309,29 @@ module ClaudeAgentSDK
|
|
|
278
309
|
|
|
279
310
|
# PreToolUse hook input
|
|
280
311
|
class PreToolUseHookInput < BaseHookInput
|
|
281
|
-
attr_accessor :hook_event_name, :tool_name, :tool_input
|
|
312
|
+
attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_use_id
|
|
282
313
|
|
|
283
|
-
def initialize(hook_event_name: 'PreToolUse', tool_name: nil, tool_input: nil, **base_args)
|
|
314
|
+
def initialize(hook_event_name: 'PreToolUse', tool_name: nil, tool_input: nil, tool_use_id: nil, **base_args)
|
|
284
315
|
super(**base_args)
|
|
285
316
|
@hook_event_name = hook_event_name
|
|
286
317
|
@tool_name = tool_name
|
|
287
318
|
@tool_input = tool_input
|
|
319
|
+
@tool_use_id = tool_use_id
|
|
288
320
|
end
|
|
289
321
|
end
|
|
290
322
|
|
|
291
323
|
# PostToolUse hook input
|
|
292
324
|
class PostToolUseHookInput < BaseHookInput
|
|
293
|
-
attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_response
|
|
325
|
+
attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_response, :tool_use_id
|
|
294
326
|
|
|
295
|
-
def initialize(hook_event_name: 'PostToolUse', tool_name: nil, tool_input: nil, tool_response: nil,
|
|
327
|
+
def initialize(hook_event_name: 'PostToolUse', tool_name: nil, tool_input: nil, tool_response: nil,
|
|
328
|
+
tool_use_id: nil, **base_args)
|
|
296
329
|
super(**base_args)
|
|
297
330
|
@hook_event_name = hook_event_name
|
|
298
331
|
@tool_name = tool_name
|
|
299
332
|
@tool_input = tool_input
|
|
300
333
|
@tool_response = tool_response
|
|
334
|
+
@tool_use_id = tool_use_id
|
|
301
335
|
end
|
|
302
336
|
end
|
|
303
337
|
|
|
@@ -407,13 +441,16 @@ module ClaudeAgentSDK
|
|
|
407
441
|
|
|
408
442
|
# PreToolUse hook specific output
|
|
409
443
|
class PreToolUseHookSpecificOutput
|
|
410
|
-
attr_accessor :hook_event_name, :permission_decision, :permission_decision_reason,
|
|
444
|
+
attr_accessor :hook_event_name, :permission_decision, :permission_decision_reason,
|
|
445
|
+
:updated_input, :additional_context
|
|
411
446
|
|
|
412
|
-
def initialize(permission_decision: nil, permission_decision_reason: nil, updated_input: nil
|
|
447
|
+
def initialize(permission_decision: nil, permission_decision_reason: nil, updated_input: nil,
|
|
448
|
+
additional_context: nil)
|
|
413
449
|
@hook_event_name = 'PreToolUse'
|
|
414
450
|
@permission_decision = permission_decision # 'allow', 'deny', or 'ask'
|
|
415
451
|
@permission_decision_reason = permission_decision_reason
|
|
416
452
|
@updated_input = updated_input
|
|
453
|
+
@additional_context = additional_context
|
|
417
454
|
end
|
|
418
455
|
|
|
419
456
|
def to_h
|
|
@@ -421,6 +458,7 @@ module ClaudeAgentSDK
|
|
|
421
458
|
result[:permissionDecision] = @permission_decision if @permission_decision
|
|
422
459
|
result[:permissionDecisionReason] = @permission_decision_reason if @permission_decision_reason
|
|
423
460
|
result[:updatedInput] = @updated_input if @updated_input
|
|
461
|
+
result[:additionalContext] = @additional_context if @additional_context
|
|
424
462
|
result
|
|
425
463
|
end
|
|
426
464
|
end
|
|
@@ -791,7 +829,8 @@ module ClaudeAgentSDK
|
|
|
791
829
|
:fork_session, :agents, :setting_sources,
|
|
792
830
|
:output_format, :max_budget_usd, :max_thinking_tokens,
|
|
793
831
|
:fallback_model, :plugins, :debug_stderr,
|
|
794
|
-
:betas, :tools, :sandbox, :enable_file_checkpointing, :append_allowed_tools
|
|
832
|
+
:betas, :tools, :sandbox, :enable_file_checkpointing, :append_allowed_tools,
|
|
833
|
+
:thinking, :effort
|
|
795
834
|
|
|
796
835
|
# Non-nil defaults for options that need them.
|
|
797
836
|
# Keys absent from here default to nil.
|
|
@@ -853,13 +892,14 @@ module ClaudeAgentSDK
|
|
|
853
892
|
|
|
854
893
|
# SDK MCP Tool definition
|
|
855
894
|
class SdkMcpTool
|
|
856
|
-
attr_accessor :name, :description, :input_schema, :handler
|
|
895
|
+
attr_accessor :name, :description, :input_schema, :handler, :annotations
|
|
857
896
|
|
|
858
|
-
def initialize(name:, description:, input_schema:, handler:)
|
|
897
|
+
def initialize(name:, description:, input_schema:, handler:, annotations: nil)
|
|
859
898
|
@name = name
|
|
860
899
|
@description = description
|
|
861
900
|
@input_schema = input_schema
|
|
862
901
|
@handler = handler
|
|
902
|
+
@annotations = annotations # MCP tool annotations (e.g., { title: '...', readOnlyHint: true })
|
|
863
903
|
end
|
|
864
904
|
end
|
|
865
905
|
|
data/lib/claude_agent_sdk.rb
CHANGED
|
@@ -65,12 +65,46 @@ module ClaudeAgentSDK
|
|
|
65
65
|
options = options.dup_with(env: (options.env || {}).merge('CLAUDE_CODE_ENTRYPOINT' => 'sdk-rb'))
|
|
66
66
|
|
|
67
67
|
Async do
|
|
68
|
-
|
|
68
|
+
# Always use streaming mode with control protocol (matches Python SDK).
|
|
69
|
+
# This sends agents via initialize request instead of CLI args,
|
|
70
|
+
# avoiding OS ARG_MAX limits.
|
|
71
|
+
transport = SubprocessCLITransport.new(options)
|
|
69
72
|
begin
|
|
70
73
|
transport.connect
|
|
71
74
|
|
|
72
|
-
#
|
|
73
|
-
|
|
75
|
+
# Extract SDK MCP servers
|
|
76
|
+
sdk_mcp_servers = {}
|
|
77
|
+
if options.mcp_servers.is_a?(Hash)
|
|
78
|
+
options.mcp_servers.each do |name, config|
|
|
79
|
+
sdk_mcp_servers[name] = config[:instance] if config.is_a?(Hash) && config[:type] == 'sdk'
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Create Query handler for control protocol
|
|
84
|
+
query_handler = Query.new(
|
|
85
|
+
transport: transport,
|
|
86
|
+
is_streaming_mode: true,
|
|
87
|
+
agents: options.agents,
|
|
88
|
+
sdk_mcp_servers: sdk_mcp_servers
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Start reading messages in background
|
|
92
|
+
query_handler.start
|
|
93
|
+
|
|
94
|
+
# Initialize the control protocol (sends agents)
|
|
95
|
+
query_handler.initialize_protocol
|
|
96
|
+
|
|
97
|
+
# Send prompt(s) as user messages, then close stdin
|
|
98
|
+
if prompt.is_a?(String)
|
|
99
|
+
message = {
|
|
100
|
+
type: 'user',
|
|
101
|
+
message: { role: 'user', content: prompt },
|
|
102
|
+
parent_tool_use_id: nil,
|
|
103
|
+
session_id: 'default'
|
|
104
|
+
}
|
|
105
|
+
transport.write(JSON.generate(message) + "\n")
|
|
106
|
+
transport.end_input
|
|
107
|
+
elsif prompt.is_a?(Enumerator) || prompt.respond_to?(:each)
|
|
74
108
|
Async do
|
|
75
109
|
begin
|
|
76
110
|
prompt.each do |message_json|
|
|
@@ -82,13 +116,18 @@ module ClaudeAgentSDK
|
|
|
82
116
|
end
|
|
83
117
|
end
|
|
84
118
|
|
|
85
|
-
# Read and yield messages
|
|
86
|
-
|
|
119
|
+
# Read and yield messages from the query handler (filters out control messages)
|
|
120
|
+
query_handler.receive_messages do |data|
|
|
87
121
|
message = MessageParser.parse(data)
|
|
88
122
|
block.call(message)
|
|
89
123
|
end
|
|
90
124
|
ensure
|
|
91
|
-
|
|
125
|
+
# query_handler.close stops the background read task and closes the transport
|
|
126
|
+
if query_handler
|
|
127
|
+
query_handler.close
|
|
128
|
+
else
|
|
129
|
+
transport.close
|
|
130
|
+
end
|
|
92
131
|
end
|
|
93
132
|
end.wait
|
|
94
133
|
end
|
|
@@ -167,7 +206,7 @@ module ClaudeAgentSDK
|
|
|
167
206
|
)
|
|
168
207
|
|
|
169
208
|
# Client always uses streaming mode; keep stdin open for bidirectional communication.
|
|
170
|
-
@transport = SubprocessCLITransport.new(
|
|
209
|
+
@transport = SubprocessCLITransport.new(configured_options)
|
|
171
210
|
@transport.connect
|
|
172
211
|
|
|
173
212
|
# Extract SDK MCP servers
|
|
@@ -187,7 +226,8 @@ module ClaudeAgentSDK
|
|
|
187
226
|
is_streaming_mode: true,
|
|
188
227
|
can_use_tool: configured_options.can_use_tool,
|
|
189
228
|
hooks: hooks,
|
|
190
|
-
sdk_mcp_servers: sdk_mcp_servers
|
|
229
|
+
sdk_mcp_servers: sdk_mcp_servers,
|
|
230
|
+
agents: configured_options.agents
|
|
191
231
|
)
|
|
192
232
|
|
|
193
233
|
# Start query handler and initialize
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-02-
|
|
10
|
+
date: 2026-02-20 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async
|