claude-agent-sdk 0.8.0 → 0.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c723c3e660d135ab87311e27be6a80b8d13127f9e42789a66b1d30ec0395278
4
- data.tar.gz: 0652f808dd792a6bef8db32afc567567fe9f016abdf095f1d13ae0ac07af0d03
3
+ metadata.gz: b625dde6e1777c93be6e4bcfb6c9565087d798d414a03078f8de4184c39dfa7f
4
+ data.tar.gz: 30117f8eac89f94aabae5c37fdc12c012ed779a0f1c58062d70e9b727ee5ce3c
5
5
  SHA512:
6
- metadata.gz: 399925ff55a9a23ab54cbe97adb18fea804be6c92cf21aa3ca3fdcd4cbe54693dfc8b77b85f15ed2eaf1517acaecd09935701d0248fb99a23be6325d151c99aa
7
- data.tar.gz: dd14802c444f4d6609cbb8215e79c3f5d8c4d9453e20d0d3af40cd853d653625c93e117082118beee2c5fb0ad29447a170687a44d56f88be6785affb8d118060
6
+ metadata.gz: 2a36e3ff75415bb4c2f42b27a8280b4828199af2972d20aa5fa4366a24daefd325e5a11c3e39ee9396144561c98e2ea1ddcc94157b80fc7aeef72f58a526e36b
7
+ data.tar.gz: f0fde6a0779679869bd4d6607f7220ea016c021896a97a421be650e169ef53719d5810cf614613c7cb52b24c19ebbf2f3908f3cc49f07bc5b13aa58f377e1a8b
data/CHANGELOG.md CHANGED
@@ -5,6 +5,36 @@ 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.8.1] - 2026-03-08
9
+
10
+ Python SDK parity fixes for one-shot `query()` control protocol and CLI transport.
11
+
12
+ ### Fixed
13
+
14
+ #### One-Shot Query Control Protocol
15
+ - **Hooks and `can_use_tool` in `query()`:** One-shot `query()` now passes `hooks`, `can_use_tool`, and SDK MCP servers through to the `Query` handler, matching the Python SDK (previously these were Client-only)
16
+ - **`can_use_tool` validation:** String prompts with `can_use_tool` now raise `ArgumentError` (streaming mode required); conflicting `permission_prompt_tool_name` also raises early
17
+ - **`session_id` parity:** One-shot queries now send `session_id: ''` (was `'default'`), matching Python SDK behavior
18
+ - **Premature stdin close:** Added `wait_for_result_and_end_input` that holds stdin open until the first result when hooks or SDK MCP servers need control message exchange
19
+ - **`stream_input` stdin leak:** Moved `end_input` to `ensure` block so stdin is always closed even when the stream enumerator raises
20
+ - **`Async::Condition` race:** Added `@first_result_received` flag guard to prevent lost signals when result arrives before `wait` is called
21
+
22
+ #### CLI Transport Parity
23
+ - **File checkpointing:** Moved from deprecated `--enable-file-checkpointing` CLI flag to `CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING` environment variable
24
+ - **Partial messages:** Now also sets `CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING=1` environment variable when `include_partial_messages` is enabled
25
+ - **Tools preset:** `ToolsPreset` objects and preset hashes now map to `--tools default` (was `--tools <json>`)
26
+ - **Plugins:** Changed from `--plugins <json>` to `--plugin-dir <path>` per-plugin, matching current CLI interface
27
+ - **Plugin type:** `SdkPluginConfig` now defaults to `type: 'local'` (was `'plugin'`), normalizes legacy `'plugin'` type
28
+ - **Rewind control request:** Changed key from `userMessageUuid` to `user_message_id` for Python SDK parity
29
+ - **Settings file with sandbox:** When sandbox is enabled and settings is a file path, now reads and parses the file to merge sandbox settings (raises on missing/invalid files instead of silently dropping settings)
30
+
31
+ #### Hook Input Parsing
32
+ - **Falsy value preservation:** `parse_hook_input` now uses `key?`-based lookup instead of `||`, correctly preserving `false` and `nil` values (e.g., `stop_hook_active: false`)
33
+ - **Empty hooks normalization:** `query()` now skips empty matcher lists and normalizes hooks to `nil` when no matchers survive, preventing unnecessary 60s close-wait timeout
34
+
35
+ ### Changed
36
+ - **`build_command` refactored:** Extracted `build_settings_args`, `build_tools_args`, `build_output_format_args`, `build_mcp_servers_args`, `build_plugins_args` private helpers to reduce method complexity
37
+
8
38
  ## [0.8.0] - 2026-03-05
9
39
 
10
40
  Port of Python SDK v0.1.46 features.
@@ -22,6 +22,8 @@ module ClaudeAgentSDK
22
22
 
23
23
  CONTROL_REQUEST_TIMEOUT_ENV_VAR = 'CLAUDE_AGENT_SDK_CONTROL_REQUEST_TIMEOUT_SECONDS'
24
24
  DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS = 1200.0
25
+ STREAM_CLOSE_TIMEOUT_ENV_VAR = 'CLAUDE_CODE_STREAM_CLOSE_TIMEOUT'
26
+ DEFAULT_STREAM_CLOSE_TIMEOUT_SECONDS = 60.0
25
27
 
26
28
  def initialize(transport:, is_streaming_mode:, can_use_tool: nil, hooks: nil, sdk_mcp_servers: nil, agents: nil)
27
29
  @transport = transport
@@ -42,6 +44,8 @@ module ClaudeAgentSDK
42
44
 
43
45
  # Message stream
44
46
  @message_queue = Async::Queue.new
47
+ @first_result_received = false
48
+ @first_result_condition = Async::Condition.new
45
49
  @task = nil
46
50
  @initialized = false
47
51
  @closed = false
@@ -124,6 +128,16 @@ module ClaudeAgentSDK
124
128
  DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS
125
129
  end
126
130
 
131
+ def stream_close_timeout_seconds
132
+ raw_value = ENV.fetch(STREAM_CLOSE_TIMEOUT_ENV_VAR, nil)
133
+ return DEFAULT_STREAM_CLOSE_TIMEOUT_SECONDS if raw_value.nil? || raw_value.strip.empty?
134
+
135
+ value = Float(raw_value) / 1000.0
136
+ value.positive? ? value : DEFAULT_STREAM_CLOSE_TIMEOUT_SECONDS
137
+ rescue ArgumentError
138
+ DEFAULT_STREAM_CLOSE_TIMEOUT_SECONDS
139
+ end
140
+
127
141
  def read_messages
128
142
  @transport.read_messages do |message|
129
143
  break if @closed
@@ -150,6 +164,10 @@ module ClaudeAgentSDK
150
164
  task&.stop
151
165
  next
152
166
  else
167
+ if message[:type] == 'result' && !@first_result_received
168
+ @first_result_received = true
169
+ @first_result_condition.signal
170
+ end
153
171
  # Regular SDK messages go to the queue
154
172
  @message_queue.enqueue(message)
155
173
  end
@@ -164,6 +182,10 @@ module ClaudeAgentSDK
164
182
  # Put error in queue so iterators can handle it
165
183
  @message_queue.enqueue({ type: 'error', error: e })
166
184
  ensure
185
+ unless @first_result_received
186
+ @first_result_received = true
187
+ @first_result_condition.signal
188
+ end
167
189
  # Always signal end of stream
168
190
  @message_queue.enqueue({ type: 'end' })
169
191
  end
@@ -308,7 +330,13 @@ module ClaudeAgentSDK
308
330
 
309
331
  def parse_hook_input(input_data)
310
332
  event_name = input_data[:hook_event_name] || input_data['hook_event_name']
311
- fetch = ->(key) { input_data[key] || input_data[key.to_s] }
333
+ fetch = lambda do |key|
334
+ if input_data.key?(key)
335
+ input_data[key]
336
+ elsif input_data.key?(key.to_s)
337
+ input_data[key.to_s]
338
+ end
339
+ end
312
340
  base_args = {
313
341
  session_id: fetch.call(:session_id),
314
342
  transcript_path: fetch.call(:transcript_path),
@@ -704,20 +732,42 @@ module ClaudeAgentSDK
704
732
  def rewind_files(user_message_uuid)
705
733
  send_control_request({
706
734
  subtype: 'rewind_files',
707
- userMessageUuid: user_message_uuid
735
+ user_message_id: user_message_uuid
708
736
  })
709
737
  end
710
738
 
739
+ # Wait for the first result before closing stdin when hooks or SDK MCP
740
+ # servers may still need to exchange control messages with the CLI.
741
+ def wait_for_result_and_end_input
742
+ if !@first_result_received &&
743
+ ((@sdk_mcp_servers && !@sdk_mcp_servers.empty?) || (@hooks && !@hooks.empty?))
744
+ Async::Task.current.with_timeout(stream_close_timeout_seconds) do
745
+ @first_result_condition.wait unless @first_result_received
746
+ end
747
+ end
748
+ rescue Async::TimeoutError
749
+ nil
750
+ ensure
751
+ @transport.end_input
752
+ end
753
+
711
754
  # Stream input messages to transport
712
755
  def stream_input(stream)
713
756
  stream.each do |message|
714
757
  break if @closed
715
- @transport.write(JSON.generate(message) + "\n")
758
+ serialized = if message.is_a?(Hash)
759
+ JSON.generate(message) + "\n"
760
+ else
761
+ message.to_s
762
+ end
763
+ serialized += "\n" unless serialized.end_with?("\n")
764
+ @transport.write(serialized)
716
765
  end
717
- @transport.end_input
718
766
  rescue StandardError => e
719
767
  # Log error but don't raise
720
768
  warn "Error streaming input: #{e.message}"
769
+ ensure
770
+ wait_for_result_and_end_input
721
771
  end
722
772
 
723
773
  # Receive SDK messages (not control messages)
@@ -98,46 +98,7 @@ module ClaudeAgentSDK
98
98
  cmd.concat(['--resume', @options.resume]) if @options.resume
99
99
 
100
100
  # Settings handling with sandbox merge
101
- # Sandbox settings are merged into the main settings JSON
102
- if @options.settings || @options.sandbox
103
- settings_hash = {}
104
- settings_is_path = false
105
-
106
- # Parse existing settings if provided
107
- if @options.settings
108
- if @options.settings.is_a?(String)
109
- begin
110
- settings_hash = JSON.parse(@options.settings)
111
- rescue JSON::ParserError
112
- # If not valid JSON, treat as file path and pass as-is
113
- settings_is_path = true
114
- cmd.concat(['--settings', @options.settings])
115
- if @options.sandbox
116
- warn "Warning: Cannot merge sandbox settings when settings is a file path. " \
117
- "Sandbox settings will be ignored. Use a Hash or JSON string for settings " \
118
- "to enable sandbox merging."
119
- end
120
- end
121
- elsif @options.settings.is_a?(Hash)
122
- settings_hash = @options.settings.dup
123
- end
124
- end
125
-
126
- # Merge sandbox settings if provided (only when settings is not a file path)
127
- if !settings_is_path && @options.sandbox
128
- sandbox_hash = if @options.sandbox.is_a?(SandboxSettings)
129
- @options.sandbox.to_h
130
- else
131
- @options.sandbox
132
- end
133
- settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
134
- end
135
-
136
- # Output merged settings (only when settings is not a file path)
137
- if !settings_is_path && !settings_hash.empty?
138
- cmd.concat(['--settings', JSON.generate(settings_hash)])
139
- end
140
- end
101
+ build_settings_args(cmd)
141
102
 
142
103
  # Budget limit option
143
104
  cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
@@ -155,39 +116,15 @@ module ClaudeAgentSDK
155
116
  end
156
117
 
157
118
  # Tools option for base tools selection
158
- if @options.tools
159
- if @options.tools.is_a?(Array)
160
- cmd.concat(['--tools', @options.tools.join(',')])
161
- elsif @options.tools.is_a?(ToolsPreset)
162
- cmd.concat(['--tools', JSON.generate(@options.tools.to_h)])
163
- elsif @options.tools.is_a?(Hash)
164
- cmd.concat(['--tools', JSON.generate(@options.tools)])
165
- end
166
- end
119
+ build_tools_args(cmd)
167
120
 
168
121
  # Append allowed tools option
169
122
  if @options.append_allowed_tools && !@options.append_allowed_tools.empty?
170
123
  cmd.concat(['--append-allowed-tools', @options.append_allowed_tools.join(',')])
171
124
  end
172
125
 
173
- # File checkpointing for rewind support
174
- cmd << '--enable-file-checkpointing' if @options.enable_file_checkpointing
175
-
176
126
  # JSON schema for structured output
177
- # Accepts either:
178
- # 1. Direct schema: { type: 'object', properties: {...} }
179
- # 2. Wrapped format: { type: 'json_schema', schema: {...} }
180
- if @options.output_format
181
- schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == 'json_schema'
182
- @options.output_format[:schema]
183
- elsif @options.output_format.is_a?(Hash) && @options.output_format['type'] == 'json_schema'
184
- @options.output_format['schema']
185
- else
186
- @options.output_format
187
- end
188
- schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
189
- cmd.concat(['--json-schema', schema_json])
190
- end
127
+ build_output_format_args(cmd)
191
128
 
192
129
  # Add directories
193
130
  @options.add_dirs.each do |dir|
@@ -195,23 +132,7 @@ module ClaudeAgentSDK
195
132
  end
196
133
 
197
134
  # MCP servers
198
- if @options.mcp_servers && !@options.mcp_servers.empty?
199
- if @options.mcp_servers.is_a?(Hash)
200
- servers_for_cli = {}
201
- @options.mcp_servers.each do |name, config|
202
- if config.is_a?(Hash) && config[:type] == 'sdk'
203
- # For SDK servers, exclude instance field
204
- sdk_config = config.reject { |k, _| k == :instance }
205
- servers_for_cli[name] = sdk_config
206
- else
207
- servers_for_cli[name] = config
208
- end
209
- end
210
- cmd.concat(['--mcp-config', JSON.generate({ mcpServers: servers_for_cli })]) unless servers_for_cli.empty?
211
- else
212
- cmd.concat(['--mcp-config', @options.mcp_servers.to_s])
213
- end
214
- end
135
+ build_mcp_servers_args(cmd)
215
136
 
216
137
  cmd << '--include-partial-messages' if @options.include_partial_messages
217
138
  cmd << '--fork-session' if @options.fork_session
@@ -220,12 +141,7 @@ module ClaudeAgentSDK
220
141
  # to avoid OS ARG_MAX limits with large agent configurations.
221
142
 
222
143
  # Plugins
223
- if @options.plugins && !@options.plugins.empty?
224
- plugins_config = @options.plugins.map do |plugin|
225
- plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
226
- end
227
- cmd.concat(['--plugins', JSON.generate(plugins_config)])
228
- end
144
+ build_plugins_args(cmd)
229
145
 
230
146
  # Setting sources
231
147
  sources_value = @options.setting_sources ? @options.setting_sources.join(',') : ''
@@ -264,6 +180,10 @@ module ClaudeAgentSDK
264
180
  # the env hash on top of the parent environment; a nil value actively unsets.
265
181
  process_env = ENV.to_h.merge('CLAUDECODE' => nil, 'CLAUDE_AGENT_SDK_VERSION' => VERSION).merge(custom_env)
266
182
  process_env['CLAUDE_CODE_ENTRYPOINT'] ||= 'sdk-rb'
183
+ process_env['CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING'] = 'true' if @options.enable_file_checkpointing
184
+ if @options.include_partial_messages
185
+ process_env['CLAUDE_CODE_ENABLE_FINE_GRAINED_TOOL_STREAMING'] ||= '1'
186
+ end
267
187
  process_env['PWD'] = @cwd.to_s if @cwd
268
188
 
269
189
  # Determine stderr handling
@@ -554,6 +474,107 @@ module ClaudeAgentSDK
554
474
 
555
475
  private
556
476
 
477
+ def build_settings_args(cmd)
478
+ return unless @options.settings || @options.sandbox
479
+
480
+ settings_hash = {}
481
+ settings_is_path = false
482
+
483
+ if @options.settings
484
+ if @options.settings.is_a?(String)
485
+ begin
486
+ settings_hash = JSON.parse(@options.settings)
487
+ rescue JSON::ParserError
488
+ if @options.sandbox
489
+ settings_hash = load_settings_file(@options.settings)
490
+ else
491
+ settings_is_path = true
492
+ cmd.concat(['--settings', @options.settings])
493
+ end
494
+ end
495
+ elsif @options.settings.is_a?(Hash)
496
+ settings_hash = @options.settings.dup
497
+ end
498
+ end
499
+
500
+ if !settings_is_path && @options.sandbox
501
+ sandbox_hash = @options.sandbox.is_a?(SandboxSettings) ? @options.sandbox.to_h : @options.sandbox
502
+ settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
503
+ end
504
+
505
+ cmd.concat(['--settings', JSON.generate(settings_hash)]) if !settings_is_path && !settings_hash.empty?
506
+ end
507
+
508
+ def build_tools_args(cmd)
509
+ return if @options.tools.nil?
510
+
511
+ if @options.tools.is_a?(Array)
512
+ tools_value = @options.tools.empty? ? '' : @options.tools.join(',')
513
+ cmd.concat(['--tools', tools_value])
514
+ elsif @options.tools.is_a?(ToolsPreset)
515
+ cmd.concat(['--tools', 'default'])
516
+ elsif @options.tools.is_a?(Hash)
517
+ if (@options.tools[:type] || @options.tools['type']) == 'preset'
518
+ cmd.concat(['--tools', 'default'])
519
+ else
520
+ cmd.concat(['--tools', JSON.generate(@options.tools)])
521
+ end
522
+ end
523
+ end
524
+
525
+ def build_output_format_args(cmd)
526
+ return unless @options.output_format
527
+
528
+ schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == 'json_schema'
529
+ @options.output_format[:schema]
530
+ elsif @options.output_format.is_a?(Hash) && @options.output_format['type'] == 'json_schema'
531
+ @options.output_format['schema']
532
+ else
533
+ @options.output_format
534
+ end
535
+ schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
536
+ cmd.concat(['--json-schema', schema_json])
537
+ end
538
+
539
+ def build_mcp_servers_args(cmd)
540
+ return unless @options.mcp_servers && !@options.mcp_servers.empty?
541
+
542
+ if @options.mcp_servers.is_a?(Hash)
543
+ servers_for_cli = {}
544
+ @options.mcp_servers.each do |name, config|
545
+ servers_for_cli[name] = if config.is_a?(Hash) && config[:type] == 'sdk'
546
+ config.reject { |k, _| k == :instance }
547
+ else
548
+ config
549
+ end
550
+ end
551
+ cmd.concat(['--mcp-config', JSON.generate({ mcpServers: servers_for_cli })]) unless servers_for_cli.empty?
552
+ else
553
+ cmd.concat(['--mcp-config', @options.mcp_servers.to_s])
554
+ end
555
+ end
556
+
557
+ def build_plugins_args(cmd)
558
+ return unless @options.plugins && !@options.plugins.empty?
559
+
560
+ @options.plugins.each do |plugin|
561
+ plugin_config = plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
562
+ plugin_type = plugin_config[:type] || plugin_config['type']
563
+ plugin_path = plugin_config[:path] || plugin_config['path']
564
+
565
+ raise ArgumentError, "Unsupported plugin type: #{plugin_type.inspect}" unless %w[local plugin].include?(plugin_type)
566
+ next unless plugin_path
567
+
568
+ cmd.concat(['--plugin-dir', plugin_path])
569
+ end
570
+ end
571
+
572
+ def load_settings_file(path)
573
+ raise CLIConnectionError, "Settings file not found: #{path}" unless File.file?(path)
574
+
575
+ JSON.parse(File.read(path))
576
+ end
577
+
557
578
  def resolve_thinking_tokens
558
579
  if @options.thinking
559
580
  case @options.thinking
@@ -866,8 +866,10 @@ module ClaudeAgentSDK
866
866
  class SdkPluginConfig
867
867
  attr_accessor :type, :path
868
868
 
869
- def initialize(path:)
870
- @type = 'plugin'
869
+ def initialize(path:, type: 'local')
870
+ raise ArgumentError, "unsupported plugin type: #{type}" unless %w[local plugin].include?(type)
871
+
872
+ @type = 'local'
871
873
  @path = path
872
874
  end
873
875
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '0.8.0'
4
+ VERSION = '0.8.1'
5
5
  end
@@ -82,29 +82,66 @@ module ClaudeAgentSDK
82
82
  return enum_for(:query, prompt: prompt, options: options) unless block
83
83
 
84
84
  options ||= ClaudeAgentOptions.new
85
- options = options.dup_with(env: (options.env || {}).merge('CLAUDE_CODE_ENTRYPOINT' => 'sdk-rb'))
85
+
86
+ configured_options = options
87
+ if options.can_use_tool
88
+ if prompt.is_a?(String)
89
+ raise ArgumentError,
90
+ 'can_use_tool callback requires streaming mode. Please provide prompt as an Enumerator instead of a String.'
91
+ end
92
+
93
+ raise ArgumentError, 'can_use_tool callback cannot be used with permission_prompt_tool_name' if options.permission_prompt_tool_name
94
+
95
+ configured_options = options.dup_with(permission_prompt_tool_name: 'stdio')
96
+ end
97
+
98
+ configured_options = configured_options.dup_with(
99
+ env: (configured_options.env || {}).merge('CLAUDE_CODE_ENTRYPOINT' => 'sdk-rb')
100
+ )
86
101
 
87
102
  Async do
88
103
  # Always use streaming mode with control protocol (matches Python SDK).
89
104
  # This sends agents via initialize request instead of CLI args,
90
105
  # avoiding OS ARG_MAX limits.
91
- transport = SubprocessCLITransport.new(options)
106
+ transport = SubprocessCLITransport.new(configured_options)
92
107
  begin
93
108
  transport.connect
94
109
 
95
110
  # Extract SDK MCP servers
96
111
  sdk_mcp_servers = {}
97
- if options.mcp_servers.is_a?(Hash)
98
- options.mcp_servers.each do |name, config|
112
+ if configured_options.mcp_servers.is_a?(Hash)
113
+ configured_options.mcp_servers.each do |name, config|
99
114
  sdk_mcp_servers[name] = config[:instance] if config.is_a?(Hash) && config[:type] == 'sdk'
100
115
  end
101
116
  end
102
117
 
118
+ hooks = nil
119
+ if configured_options.hooks
120
+ hooks = {}
121
+ configured_options.hooks.each do |event, matchers|
122
+ next if matchers.nil? || matchers.empty?
123
+
124
+ entries = []
125
+ matchers.each do |matcher|
126
+ config = {
127
+ matcher: matcher.matcher,
128
+ hooks: matcher.hooks
129
+ }
130
+ config[:timeout] = matcher.timeout if matcher.timeout
131
+ entries << config
132
+ end
133
+ hooks[event.to_s] = entries unless entries.empty?
134
+ end
135
+ hooks = nil if hooks.empty?
136
+ end
137
+
103
138
  # Create Query handler for control protocol
104
139
  query_handler = Query.new(
105
140
  transport: transport,
106
141
  is_streaming_mode: true,
107
- agents: options.agents,
142
+ can_use_tool: configured_options.can_use_tool,
143
+ hooks: hooks,
144
+ agents: configured_options.agents,
108
145
  sdk_mcp_servers: sdk_mcp_servers
109
146
  )
110
147
 
@@ -120,19 +157,13 @@ module ClaudeAgentSDK
120
157
  type: 'user',
121
158
  message: { role: 'user', content: prompt },
122
159
  parent_tool_use_id: nil,
123
- session_id: 'default'
160
+ session_id: ''
124
161
  }
125
162
  transport.write(JSON.generate(message) + "\n")
126
- transport.end_input
163
+ query_handler.wait_for_result_and_end_input
127
164
  elsif prompt.is_a?(Enumerator) || prompt.respond_to?(:each)
128
165
  Async do
129
- begin
130
- prompt.each do |message_json|
131
- transport.write(message_json)
132
- end
133
- ensure
134
- transport.end_input
135
- end
166
+ query_handler.stream_input(prompt)
136
167
  end
137
168
  end
138
169
 
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.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Community Contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-05 00:00:00.000000000 Z
10
+ date: 2026-03-07 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async
@@ -123,6 +123,7 @@ metadata:
123
123
  source_code_uri: https://github.com/ya-luotao/claude-agent-sdk-ruby
124
124
  changelog_uri: https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/CHANGELOG.md
125
125
  documentation_uri: https://docs.anthropic.com/en/docs/claude-code/sdk
126
+ allowed_push_host: https://rubygems.org
126
127
  rdoc_options: []
127
128
  require_paths:
128
129
  - lib