claude-agent-sdk 0.4.2 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db31632430bb28dc8f07903bfc8634a8c437da9dc988f2237606948ecc71f7e0
4
- data.tar.gz: e7574810137063aef37a0a78fb9d855645a0a479ba0b909b63a491f0ea7060f6
3
+ metadata.gz: 8934de13efa22ba9e3e679393dfb3ef39e8eed194a9db4af8ef5981c8fa66570
4
+ data.tar.gz: 79bfbe222fb677a49be4249e00bdc5148a43e9e0a9bac8e9e4e7fa7d3a34abd7
5
5
  SHA512:
6
- metadata.gz: e394102a18bace179f7745b2b10cc721393ff25e8dcbc75f9492e6822aa6a80afa1455eb168b6567857cbf10a7d1d2e96e7293e43b043ad69ac2b41f95a3eb99
7
- data.tar.gz: 03b94423b5d2369eb974e9cf82b2523e36aea4a09d05181e86528da11b3432d8685256705989210941b644333b5f9d3173b2efa0fd3b5f1b74e5e5aea3705bc6
6
+ metadata.gz: c3c722b1a6ab8ff9ca7484a7b6fd59c1d60660d6fb1e111864c2afe81852bb913c5cc9bf241eb7ad635686b9d1dd8cc6ff849f2a2b6acf2eec3546c09e0e7de2
7
+ data.tar.gz: 2ae9442e915901908b5c48496a348479685273701182a18ace8a20402b1e15c8630b38fd82f2ea480bf97af4e5c25c697711f5ffcc768bf15868081b9b5f24e4
data/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ 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.6.0] - 2026-02-13
9
+
10
+ ### Added
11
+ - **Configurable control request timeout:** New `CLAUDE_AGENT_SDK_CONTROL_REQUEST_TIMEOUT_SECONDS` environment variable (default 1200s) for tuning the control protocol timeout — essential for long-running agent sessions and agent teams
12
+ - **`ControlRequestTimeoutError`:** Dedicated error class (`< CLIConnectionError`) raised on control request timeouts, enabling typed exception handling instead of string matching
13
+
14
+ ### Fixed
15
+ - **camelCase `requestId` fallback:** All control message routing (`read_messages`, `handle_control_response`, `handle_control_request`) now tolerates both `request_id` and `requestId` keys from the CLI
16
+ - **Outbound `requestId` parity:** Control requests and responses now include both `request_id` and `requestId` for maximum CLI compatibility
17
+ - **Pending request unblocking:** Transport errors now signal all pending control request conditions, preventing callers from hanging until timeout
18
+ - **Error object in message queue:** `read_messages` rescue now enqueues the exception object (not just `e.message`), preserving error class for typed handling
19
+ - **Thread-safe CLI discovery:** `find_cli` uses `Open3.capture2` instead of backtick shell for thread safety
20
+ - **Robust process cleanup:** `close` now uses SIGTERM → 2s grace period → SIGKILL escalation (was immediate SIGTERM with no fallback), handles `Errno::ESRCH` for already-dead processes, and logs cleanup warnings instead of silently swallowing errors
21
+
22
+ ## [0.5.0] - 2026-02-07
23
+
24
+ ### Added
25
+ - **Default configuration:** `ClaudeAgentSDK.configure` block for setting default options that merge with every `ClaudeAgentOptions` instance, ideal for Rails initializers (PR #8)
26
+ - `ClaudeAgentSDK.reset_configuration` for resetting defaults (useful in tests)
27
+ - Deep merge for `env` and `mcp_servers` hashes; provided values override configured defaults
28
+ - `OPTION_DEFAULTS` constant on `ClaudeAgentOptions` for introspectable non-nil defaults
29
+
30
+ ### Changed
31
+ - `ClaudeAgentOptions#initialize` now uses `**kwargs` internally to correctly distinguish caller-provided values from method signature defaults
32
+
8
33
  ## [0.4.2] - 2026-02-07
9
34
 
10
35
  ### Fixed
data/README.md CHANGED
@@ -38,7 +38,7 @@ Add this line to your application's Gemfile:
38
38
  gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
39
39
 
40
40
  # Or use a stable version from RubyGems
41
- gem 'claude-agent-sdk', '~> 0.4.0'
41
+ gem 'claude-agent-sdk', '~> 0.6.0'
42
42
  ```
43
43
 
44
44
  And then execute:
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgentSDK
4
+ # Configuration class for setting default options
5
+ #
6
+ # Use this to set default options that will be merged with every request.
7
+ # This is especially useful in Rails applications where you want to
8
+ # configure defaults once during initialization.
9
+ #
10
+ # @example In a Rails initializer (config/initializers/claude_agent_sdk.rb)
11
+ # ClaudeAgentSDK.configure do |config|
12
+ # config.default_options = {
13
+ # env: {
14
+ # 'ANTHROPIC_API_KEY' => ENV['ANTHROPIC_API_KEY'],
15
+ # 'CUSTOM_VAR' => 'value'
16
+ # },
17
+ # permission_mode: 'bypassPermissions',
18
+ # model: 'sonnet'
19
+ # }
20
+ # end
21
+ #
22
+ # @example Then use ClaudeAgentSDK without repeating options
23
+ # # env and other defaults will be automatically applied
24
+ # ClaudeAgentSDK.query(prompt: "Hello!")
25
+ #
26
+ # # You can still override defaults when needed
27
+ # ClaudeAgentSDK.query(
28
+ # prompt: "Hello!",
29
+ # options: ClaudeAgentOptions.new(model: 'opus') # overrides default
30
+ # )
31
+ class Configuration
32
+ attr_accessor :default_options
33
+
34
+ def initialize
35
+ @default_options = {}
36
+ end
37
+ end
38
+
39
+ class << self
40
+ # Configure the SDK with default options
41
+ #
42
+ # @yield [Configuration] The configuration object
43
+ #
44
+ # @example Set default env and other options
45
+ # ClaudeAgentSDK.configure do |config|
46
+ # config.default_options = {
47
+ # env: { 'API_KEY' => 'xxx' },
48
+ # permission_mode: 'bypassPermissions'
49
+ # }
50
+ # end
51
+ def configure
52
+ yield(configuration)
53
+ end
54
+
55
+ # Get the configuration object
56
+ #
57
+ # @return [Configuration] The current configuration
58
+ def configuration
59
+ @configuration ||= Configuration.new
60
+ end
61
+
62
+ # Reset configuration to defaults (useful for testing)
63
+ def reset_configuration
64
+ @configuration = Configuration.new
65
+ end
66
+
67
+ # Get merged default options for use with ClaudeAgentOptions
68
+ #
69
+ # @return [Hash] Default options hash
70
+ def default_options
71
+ configuration.default_options || {}
72
+ end
73
+ end
74
+ end
@@ -7,6 +7,9 @@ module ClaudeAgentSDK
7
7
  # Raised when unable to connect to Claude Code
8
8
  class CLIConnectionError < ClaudeSDKError; end
9
9
 
10
+ # Raised when the control protocol does not respond in time
11
+ class ControlRequestTimeoutError < CLIConnectionError; end
12
+
10
13
  # Raised when Claude Code is not found or not installed
11
14
  class CLINotFoundError < CLIConnectionError
12
15
  def initialize(message = 'Claude Code not found', cli_path: nil)
@@ -6,6 +6,7 @@ require 'async/queue'
6
6
  require 'async/condition'
7
7
  require 'securerandom'
8
8
  require_relative 'transport'
9
+ require_relative 'errors'
9
10
 
10
11
  module ClaudeAgentSDK
11
12
  # Handles bidirectional control protocol on top of Transport
@@ -19,6 +20,9 @@ module ClaudeAgentSDK
19
20
  class Query
20
21
  attr_reader :transport, :is_streaming_mode, :sdk_mcp_servers
21
22
 
23
+ CONTROL_REQUEST_TIMEOUT_ENV_VAR = 'CLAUDE_AGENT_SDK_CONTROL_REQUEST_TIMEOUT_SECONDS'
24
+ DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS = 1200.0
25
+
22
26
  def initialize(transport:, is_streaming_mode:, can_use_tool: nil, hooks: nil, sdk_mcp_servers: nil)
23
27
  @transport = transport
24
28
  @is_streaming_mode = is_streaming_mode
@@ -95,6 +99,16 @@ module ClaudeAgentSDK
95
99
 
96
100
  private
97
101
 
102
+ def control_request_timeout_seconds
103
+ raw_value = ENV.fetch(CONTROL_REQUEST_TIMEOUT_ENV_VAR, nil)
104
+ return DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS if raw_value.nil? || raw_value.strip.empty?
105
+
106
+ value = Float(raw_value)
107
+ value.positive? ? value : DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS
108
+ rescue ArgumentError
109
+ DEFAULT_CONTROL_REQUEST_TIMEOUT_SECONDS
110
+ end
111
+
98
112
  def read_messages
99
113
  @transport.read_messages do |message|
100
114
  break if @closed
@@ -106,7 +120,7 @@ module ClaudeAgentSDK
106
120
  when 'control_response'
107
121
  handle_control_response(message)
108
122
  when 'control_request'
109
- request_id = message[:request_id]
123
+ request_id = message[:request_id] || message[:requestId]
110
124
  task = Async do
111
125
  begin
112
126
  handle_control_request(message)
@@ -126,8 +140,14 @@ module ClaudeAgentSDK
126
140
  end
127
141
  end
128
142
  rescue StandardError => e
143
+ # Unblock pending control requests (e.g., initialize) so callers don't hang until timeout.
144
+ @pending_control_responses.dup.each do |request_id, condition|
145
+ @pending_control_results[request_id] ||= e
146
+ condition.signal
147
+ end
148
+
129
149
  # Put error in queue so iterators can handle it
130
- @message_queue.enqueue({ type: 'error', error: e.message })
150
+ @message_queue.enqueue({ type: 'error', error: e })
131
151
  ensure
132
152
  # Always signal end of stream
133
153
  @message_queue.enqueue({ type: 'end' })
@@ -135,7 +155,7 @@ module ClaudeAgentSDK
135
155
 
136
156
  def handle_control_response(message)
137
157
  response = message[:response] || {}
138
- request_id = response[:request_id]
158
+ request_id = response[:request_id] || response[:requestId] || message[:request_id] || message[:requestId]
139
159
  return unless @pending_control_responses.key?(request_id)
140
160
 
141
161
  if response[:subtype] == 'error'
@@ -149,7 +169,7 @@ module ClaudeAgentSDK
149
169
  end
150
170
 
151
171
  def handle_control_request(request)
152
- request_id = request[:request_id]
172
+ request_id = request[:request_id] || request[:requestId]
153
173
  request_data = request[:request]
154
174
  subtype = request_data[:subtype]
155
175
 
@@ -172,6 +192,7 @@ module ClaudeAgentSDK
172
192
  response: {
173
193
  subtype: 'success',
174
194
  request_id: request_id,
195
+ requestId: request_id,
175
196
  response: response_data
176
197
  }
177
198
  }
@@ -183,6 +204,7 @@ module ClaudeAgentSDK
183
204
  response: {
184
205
  subtype: 'error',
185
206
  request_id: request_id,
207
+ requestId: request_id,
186
208
  error: 'Cancelled'
187
209
  }
188
210
  }
@@ -194,6 +216,7 @@ module ClaudeAgentSDK
194
216
  response: {
195
217
  subtype: 'error',
196
218
  request_id: request_id,
219
+ requestId: request_id,
197
220
  error: e.message
198
221
  }
199
222
  }
@@ -398,6 +421,8 @@ module ClaudeAgentSDK
398
421
  def send_control_request(request)
399
422
  raise 'Control requests require streaming mode' unless @is_streaming_mode
400
423
 
424
+ timeout_seconds = control_request_timeout_seconds
425
+
401
426
  # Generate unique request ID
402
427
  @request_counter += 1
403
428
  request_id = "req_#{@request_counter}_#{SecureRandom.hex(4)}"
@@ -410,14 +435,15 @@ module ClaudeAgentSDK
410
435
  control_request = {
411
436
  type: 'control_request',
412
437
  request_id: request_id,
438
+ requestId: request_id,
413
439
  request: request
414
440
  }
415
441
 
416
442
  @transport.write(JSON.generate(control_request) + "\n")
417
443
 
418
- # Wait for response with timeout
444
+ # Wait for response with timeout (default 1200s to handle slow CLI startup)
419
445
  Async do |task|
420
- task.with_timeout(60.0) do
446
+ task.with_timeout(timeout_seconds) do
421
447
  condition.wait
422
448
  end
423
449
 
@@ -431,7 +457,7 @@ module ClaudeAgentSDK
431
457
  rescue Async::TimeoutError
432
458
  @pending_control_responses.delete(request_id)
433
459
  @pending_control_results.delete(request_id)
434
- raise "Control request timeout: #{request[:subtype]}"
460
+ raise ControlRequestTimeoutError, "Control request timeout: #{request[:subtype]}"
435
461
  end
436
462
 
437
463
  def handle_sdk_mcp_request(server_name, message)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'json'
4
4
  require 'open3'
5
+ require 'timeout'
5
6
  require_relative 'transport'
6
7
  require_relative 'errors'
7
8
  require_relative 'version'
@@ -29,9 +30,15 @@ module ClaudeAgentSDK
29
30
  end
30
31
 
31
32
  def find_cli
32
- # Try which command first
33
- cli = `which claude 2>/dev/null`.strip
34
- return cli unless cli.empty?
33
+ # Try which command first (using Open3 for thread safety)
34
+ cli = nil
35
+ begin
36
+ stdout, _status = Open3.capture2('which', 'claude')
37
+ cli = stdout.strip
38
+ rescue StandardError
39
+ # which command failed, try common locations
40
+ end
41
+ return cli if cli && !cli.empty? && File.executable?(cli)
35
42
 
36
43
  # Try common locations
37
44
  locations = [
@@ -326,35 +333,67 @@ module ClaudeAgentSDK
326
333
  @ready = false
327
334
  return unless @process
328
335
 
336
+ cleanup_errors = []
337
+
329
338
  # Kill stderr thread
330
339
  if @stderr_task&.alive?
331
- @stderr_task.kill
332
- @stderr_task.join(1) rescue nil
340
+ begin
341
+ @stderr_task.kill
342
+ @stderr_task.join(1)
343
+ rescue StandardError => e
344
+ cleanup_errors << "stderr thread: #{e.message}"
345
+ end
333
346
  end
334
347
 
335
348
  # Close streams
336
349
  begin
337
350
  @stdin&.close
338
- rescue StandardError
339
- # Ignore
351
+ rescue IOError
352
+ # Already closed, ignore
353
+ rescue StandardError => e
354
+ cleanup_errors << "stdin: #{e.message}"
340
355
  end
356
+
341
357
  begin
342
358
  @stdout&.close
343
- rescue StandardError
344
- # Ignore
359
+ rescue IOError
360
+ # Already closed, ignore
361
+ rescue StandardError => e
362
+ cleanup_errors << "stdout: #{e.message}"
345
363
  end
364
+
346
365
  begin
347
366
  @stderr&.close
348
- rescue StandardError
349
- # Ignore
367
+ rescue IOError
368
+ # Already closed, ignore
369
+ rescue StandardError => e
370
+ cleanup_errors << "stderr: #{e.message}"
350
371
  end
351
372
 
352
373
  # Terminate process
353
374
  begin
354
- Process.kill('TERM', @process.pid) if @process.alive?
355
- @process.value
356
- rescue StandardError
357
- # Ignore
375
+ if @process.alive?
376
+ Process.kill('TERM', @process.pid)
377
+ # Wait briefly for graceful shutdown
378
+ Timeout.timeout(2) { @process.value }
379
+ end
380
+ rescue Timeout::Error
381
+ # Force kill if graceful shutdown failed
382
+ begin
383
+ Process.kill('KILL', @process.pid)
384
+ @process.value
385
+ rescue StandardError => e
386
+ cleanup_errors << "force kill: #{e.message}"
387
+ end
388
+ rescue Errno::ESRCH
389
+ # Process already dead, ignore
390
+ rescue StandardError => e
391
+ cleanup_errors << "process termination: #{e.message}"
392
+ end
393
+
394
+ # Log any cleanup errors (non-fatal)
395
+ if cleanup_errors.any?
396
+ warn "Claude SDK: Cleanup warnings: #{cleanup_errors.join(', ')}"
358
397
  end
359
398
 
360
399
  @process = nil
@@ -784,87 +784,62 @@ module ClaudeAgentSDK
784
784
  :fallback_model, :plugins, :debug_stderr,
785
785
  :betas, :tools, :sandbox, :enable_file_checkpointing, :append_allowed_tools
786
786
 
787
- def initialize(
788
- allowed_tools: [],
789
- system_prompt: nil,
790
- mcp_servers: {},
791
- permission_mode: nil,
792
- continue_conversation: false,
793
- resume: nil,
794
- max_turns: nil,
795
- disallowed_tools: [],
796
- model: nil,
797
- permission_prompt_tool_name: nil,
798
- cwd: nil,
799
- cli_path: nil,
800
- settings: nil,
801
- add_dirs: [],
802
- env: {},
803
- extra_args: {},
804
- max_buffer_size: nil,
805
- stderr: nil,
806
- can_use_tool: nil,
807
- hooks: nil,
808
- user: nil,
809
- include_partial_messages: false,
810
- fork_session: false,
811
- agents: nil,
812
- setting_sources: nil,
813
- output_format: nil,
814
- max_budget_usd: nil,
815
- max_thinking_tokens: nil,
816
- fallback_model: nil,
817
- plugins: nil,
818
- debug_stderr: nil,
819
- betas: nil,
820
- tools: nil,
821
- sandbox: nil,
822
- enable_file_checkpointing: false,
823
- append_allowed_tools: nil
824
- )
825
- @allowed_tools = allowed_tools
826
- @system_prompt = system_prompt
827
- @mcp_servers = mcp_servers
828
- @permission_mode = permission_mode
829
- @continue_conversation = continue_conversation
830
- @resume = resume
831
- @max_turns = max_turns
832
- @disallowed_tools = disallowed_tools
833
- @model = model
834
- @permission_prompt_tool_name = permission_prompt_tool_name
835
- @cwd = cwd
836
- @cli_path = cli_path
837
- @settings = settings
838
- @add_dirs = add_dirs
839
- @env = env
840
- @extra_args = extra_args
841
- @max_buffer_size = max_buffer_size
842
- @stderr = stderr
843
- @can_use_tool = can_use_tool
844
- @hooks = hooks
845
- @user = user
846
- @include_partial_messages = include_partial_messages
847
- @fork_session = fork_session
848
- @agents = agents
849
- @setting_sources = setting_sources
850
- @output_format = output_format # JSON schema for structured output
851
- @max_budget_usd = max_budget_usd # Spending cap in dollars
852
- @max_thinking_tokens = max_thinking_tokens # Extended thinking token budget
853
- @fallback_model = fallback_model # Backup model if primary unavailable
854
- @plugins = plugins # Array of SdkPluginConfig
855
- @debug_stderr = debug_stderr # Debug output file object/path
856
- @betas = betas # Array of beta feature strings (e.g., ["context-1m-2025-08-07"])
857
- @tools = tools # Base tools selection: Array, empty array [], or ToolsPreset
858
- @sandbox = sandbox # SandboxSettings instance for isolated command execution
859
- @enable_file_checkpointing = enable_file_checkpointing # Enable file checkpointing for rewind support
860
- @append_allowed_tools = append_allowed_tools # Array of tools to append to allowed_tools
787
+ # Non-nil defaults for options that need them.
788
+ # Keys absent from here default to nil.
789
+ OPTION_DEFAULTS = {
790
+ allowed_tools: [], disallowed_tools: [], add_dirs: [],
791
+ mcp_servers: {}, env: {}, extra_args: {},
792
+ continue_conversation: false, include_partial_messages: false,
793
+ fork_session: false, enable_file_checkpointing: false
794
+ }.freeze
795
+
796
+ # Valid option names derived from attr_accessor declarations.
797
+ VALID_OPTIONS = instance_methods.grep(/=\z/).map { |m| m.to_s.chomp('=').to_sym }.freeze
798
+
799
+ # Using **kwargs lets us distinguish "caller passed allowed_tools: []"
800
+ # from "caller omitted allowed_tools" — critical for correct merge with
801
+ # configured defaults.
802
+ def initialize(**kwargs)
803
+ unknown = kwargs.keys - VALID_OPTIONS
804
+ raise ArgumentError, "unknown keyword#{'s' if unknown.size > 1}: #{unknown.join(', ')}" if unknown.any?
805
+
806
+ merged = merge_with_defaults(kwargs)
807
+ OPTION_DEFAULTS.merge(merged).each do |key, value|
808
+ instance_variable_set(:"@#{key}", value)
809
+ end
861
810
  end
862
811
 
863
812
  def dup_with(**changes)
864
813
  new_options = self.dup
865
- changes.each { |key, value| new_options.send("#{key}=", value) }
814
+ changes.each { |key, value| new_options.send(:"#{key}=", value) }
866
815
  new_options
867
816
  end
817
+
818
+ private
819
+
820
+ # Merge caller-provided kwargs with configured defaults.
821
+ # Only keys the caller explicitly passed are treated as overrides;
822
+ # method-signature defaults ([], {}, false) are NOT in kwargs unless the caller wrote them.
823
+ def merge_with_defaults(kwargs)
824
+ return OPTION_DEFAULTS.merge(kwargs) unless defined?(ClaudeAgentSDK) && ClaudeAgentSDK.respond_to?(:default_options)
825
+
826
+ defaults = ClaudeAgentSDK.default_options
827
+ return OPTION_DEFAULTS.merge(kwargs) unless defaults.any?
828
+
829
+ # Start from configured defaults (deep dup hashes to prevent mutation)
830
+ result = defaults.transform_values { |v| v.is_a?(Hash) ? v.dup : v }
831
+ kwargs.each do |key, value|
832
+ default_val = result[key]
833
+ result[key] = if value.nil?
834
+ default_val # nil means "no preference" — keep the configured default
835
+ elsif default_val.is_a?(Hash) && value.is_a?(Hash)
836
+ default_val.merge(value)
837
+ else
838
+ value
839
+ end
840
+ end
841
+ OPTION_DEFAULTS.merge(result)
842
+ end
868
843
  end
869
844
 
870
845
  # SDK MCP Tool definition
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '0.4.2'
4
+ VERSION = '0.6.0'
5
5
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'claude_agent_sdk/version'
4
4
  require_relative 'claude_agent_sdk/errors'
5
+ require_relative 'claude_agent_sdk/configuration'
5
6
  require_relative 'claude_agent_sdk/types'
6
7
  require_relative 'claude_agent_sdk/transport'
7
8
  require_relative 'claude_agent_sdk/subprocess_cli_transport'
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.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Community Contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-02-06 00:00:00.000000000 Z
10
+ date: 2026-02-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async
@@ -104,6 +104,7 @@ files:
104
104
  - LICENSE
105
105
  - README.md
106
106
  - lib/claude_agent_sdk.rb
107
+ - lib/claude_agent_sdk/configuration.rb
107
108
  - lib/claude_agent_sdk/errors.rb
108
109
  - lib/claude_agent_sdk/message_parser.rb
109
110
  - lib/claude_agent_sdk/query.rb