claude-agent-sdk 0.16.5 → 0.16.7

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.
@@ -57,80 +57,148 @@ module ClaudeAgentSDK
57
57
  # Available beta features that can be enabled via the betas option
58
58
  SDK_BETAS = %w[context-1m-2025-08-07].freeze
59
59
 
60
+ # Base class for all types.
61
+ class Type
62
+ def self.wrap(object)
63
+ return object if object.is_a?(self)
64
+ return nil if object.nil?
65
+
66
+ new(object)
67
+ end
68
+
69
+ def self.from_hash(hash)
70
+ return unless hash.is_a?(Hash)
71
+
72
+ new(hash)
73
+ end
74
+
75
+ def initialize(attributes = {})
76
+ assign_attributes(attributes) if attributes
77
+ super()
78
+ end
79
+
80
+ def [](name)
81
+ read_attribute(name)
82
+ end
83
+
84
+ def []=(name, value)
85
+ assign_attribute(name, value)
86
+ end
87
+
88
+ # Subclasses should override this to return a hash representation of the object.
89
+ def to_h
90
+ {}
91
+ end
92
+
93
+ private
94
+
95
+ # Allow camelCase attribute access
96
+ def method_missing(method_name, ...)
97
+ normalized = normalize_name(method_name)
98
+
99
+ if normalized != method_name.to_s && respond_to?(normalized)
100
+ public_send(normalized, ...)
101
+ else
102
+ super
103
+ end
104
+ end
105
+
106
+ def respond_to_missing?(method_name, include_private = false)
107
+ normalized = normalize_name(method_name)
108
+ (normalized != method_name.to_s && respond_to?(normalized)) || super
109
+ end
110
+
111
+ def assign_attributes(attributes)
112
+ raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{attributes.inspect} passed." unless attributes.respond_to?(:each_pair)
113
+
114
+ return if attributes.empty?
115
+
116
+ attributes.each_pair { |name, value| assign_attribute(name, value) }
117
+ end
118
+
119
+ def assign_attribute(name, value)
120
+ setter = :"#{normalize_name(name)}="
121
+ public_send(setter, value) if respond_to?(setter)
122
+ end
123
+
124
+ def read_attribute(name)
125
+ getter = normalize_name(name)
126
+ public_send(getter) if respond_to?(getter)
127
+ end
128
+
129
+ def normalize_name(name)
130
+ name = name.dup.to_s
131
+ name.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
132
+ name.tr!("-", "_")
133
+ name.downcase!
134
+ name
135
+ end
136
+
137
+ FALSE_VALUES = [
138
+ false, 0,
139
+ "0", :'0',
140
+ "f", :f,
141
+ "F", :F,
142
+ "false", :false, # rubocop:disable Lint/BooleanSymbol
143
+ "FALSE", :FALSE,
144
+ "off", :off,
145
+ "OFF", :OFF
146
+ ].to_set.freeze
147
+
148
+ private_constant :FALSE_VALUES
149
+
150
+ def coerce_boolean(value)
151
+ return if value.nil?
152
+
153
+ if value == ""
154
+ nil
155
+ else
156
+ !FALSE_VALUES.include?(value)
157
+ end
158
+ end
159
+ end
160
+
60
161
  # Content Blocks
61
162
 
62
163
  # Text content block
63
- class TextBlock
164
+ class TextBlock < Type
64
165
  attr_accessor :text
65
-
66
- def initialize(text:)
67
- @text = text
68
- end
69
166
  end
70
167
 
71
168
  # Thinking content block
72
- class ThinkingBlock
169
+ class ThinkingBlock < Type
73
170
  attr_accessor :thinking, :signature
74
-
75
- def initialize(thinking:, signature:)
76
- @thinking = thinking
77
- @signature = signature
78
- end
79
171
  end
80
172
 
81
173
  # Tool use content block
82
- class ToolUseBlock
174
+ class ToolUseBlock < Type
83
175
  attr_accessor :id, :name, :input
84
-
85
- def initialize(id:, name:, input:)
86
- @id = id
87
- @name = name
88
- @input = input
89
- end
90
176
  end
91
177
 
92
178
  # Tool result content block
93
- class ToolResultBlock
179
+ class ToolResultBlock < Type
94
180
  attr_accessor :tool_use_id, :content, :is_error
95
-
96
- def initialize(tool_use_id:, content: nil, is_error: nil)
97
- @tool_use_id = tool_use_id
98
- @content = content
99
- @is_error = is_error
100
- end
101
181
  end
102
182
 
103
183
  # Generic content block for types the SDK doesn't explicitly handle (e.g., "document", "image").
104
184
  # Preserves the raw hash data for forward compatibility with newer CLI versions.
105
- class UnknownBlock
185
+ class UnknownBlock < Type
106
186
  attr_accessor :type, :data
107
-
108
- def initialize(type:, data:)
109
- @type = type
110
- @data = data
111
- end
112
187
  end
113
188
 
114
189
  # Message Types
115
190
 
116
191
  # User message
117
- class UserMessage
192
+ class UserMessage < Type
118
193
  attr_accessor :content, :uuid, :parent_tool_use_id, :tool_use_result
119
194
 
120
- def initialize(content:, uuid: nil, parent_tool_use_id: nil, tool_use_result: nil)
121
- @content = content
122
- @uuid = uuid # Unique identifier for rewind support
123
- @parent_tool_use_id = parent_tool_use_id
124
- @tool_use_result = tool_use_result # Tool result data when message is a tool response
125
- end
126
-
127
195
  # Concatenated text of this message. Handles both String content
128
196
  # (plain-text user prompt) and Array-of-blocks content (typed content).
129
197
  # Returns "" when there is no text.
130
198
  def text
131
- case @content
132
- when String then @content
133
- when Array then @content.grep(TextBlock).map(&:text).join("\n\n")
199
+ case content
200
+ when String then content
201
+ when Array then content.grep(TextBlock).map(&:text).join("\n\n")
134
202
  else ''
135
203
  end
136
204
  end
@@ -139,39 +207,28 @@ module ClaudeAgentSDK
139
207
  end
140
208
 
141
209
  # Assistant message with content blocks
142
- class AssistantMessage
210
+ class AssistantMessage < Type
143
211
  attr_accessor :content, :model, :parent_tool_use_id, :error, :usage,
144
212
  :message_id, :stop_reason, :session_id, :uuid
145
213
 
146
- def initialize(content:, model:, parent_tool_use_id: nil, error: nil, usage: nil,
147
- message_id: nil, stop_reason: nil, session_id: nil, uuid: nil)
148
- @content = content
149
- @model = model
150
- @parent_tool_use_id = parent_tool_use_id
151
- @error = error # One of: authentication_failed, billing_error, rate_limit, invalid_request, server_error, unknown
152
- @usage = usage # Token usage info from the API response
153
- @message_id = message_id # Unique message identifier from the API (message.id)
154
- @stop_reason = stop_reason # Why the assistant stopped (e.g., "end_turn", "max_tokens")
155
- @session_id = session_id # Session the message belongs to
156
- @uuid = uuid # Unique message UUID in the session transcript
157
- end
158
-
159
214
  # Concatenated text across every TextBlock in this message's content.
160
215
  # Returns "" when the message has no text (e.g., a pure tool_use turn).
161
216
  def text
162
- Array(@content).grep(TextBlock).map(&:text).join("\n\n")
217
+ Array(content).grep(TextBlock).map(&:text).join("\n\n")
163
218
  end
164
219
 
165
220
  alias to_s text
166
221
  end
167
222
 
168
- # System message with metadata
169
- class SystemMessage
223
+ # System message with metadata.
224
+ # When constructed from a raw CLI hash, the whole hash is stored in `#data`
225
+ # unless the caller explicitly provides a `:data` entry.
226
+ class SystemMessage < Type
170
227
  attr_accessor :subtype, :data
171
228
 
172
- def initialize(subtype:, data:)
173
- @subtype = subtype
174
- @data = data
229
+ def initialize(attributes = {})
230
+ super
231
+ @data ||= attributes if attributes.is_a?(Hash)
175
232
  end
176
233
  end
177
234
 
@@ -180,229 +237,84 @@ module ClaudeAgentSDK
180
237
  attr_accessor :uuid, :session_id, :agents, :api_key_source, :betas,
181
238
  :claude_code_version, :cwd, :tools, :mcp_servers, :model,
182
239
  :permission_mode, :slash_commands, :output_style, :skills, :plugins,
183
- :fast_mode_state
184
-
185
- def initialize(subtype:, data:, uuid: nil, session_id: nil, agents: nil,
186
- api_key_source: nil, betas: nil, claude_code_version: nil,
187
- cwd: nil, tools: nil, mcp_servers: nil, model: nil,
188
- permission_mode: nil, slash_commands: nil, output_style: nil,
189
- skills: nil, plugins: nil, fast_mode_state: nil)
190
- super(subtype: subtype, data: data)
191
- @uuid = uuid
192
- @session_id = session_id
193
- @agents = agents
194
- @api_key_source = api_key_source
195
- @betas = betas
196
- @claude_code_version = claude_code_version
197
- @cwd = cwd
198
- @tools = tools
199
- @mcp_servers = mcp_servers
200
- @model = model
201
- @permission_mode = permission_mode
202
- @slash_commands = slash_commands
203
- @output_style = output_style
204
- @skills = skills
205
- @plugins = plugins
206
- @fast_mode_state = fast_mode_state # "off", "cooldown", or "on"
207
- end
240
+ :fast_mode_state # "off", "cooldown", or "on"
208
241
  end
209
242
 
210
243
  # Compact boundary system message (emitted after context compaction completes)
211
244
  class CompactBoundaryMessage < SystemMessage
212
- attr_accessor :uuid, :session_id, :compact_metadata
245
+ attr_accessor :uuid, :session_id
246
+ attr_reader :compact_metadata
213
247
 
214
- def initialize(subtype:, data:, uuid: nil, session_id: nil, compact_metadata: nil)
215
- super(subtype: subtype, data: data)
216
- @uuid = uuid
217
- @session_id = session_id
218
- @compact_metadata = compact_metadata
248
+ def compact_metadata=(value)
249
+ @compact_metadata = value.is_a?(Hash) ? CompactMetadata.new(value) : value
219
250
  end
220
251
  end
221
252
 
222
253
  # Metadata about a compaction event
223
- class CompactMetadata
254
+ class CompactMetadata < Type
224
255
  attr_accessor :pre_tokens, :post_tokens, :trigger, :custom_instructions, :preserved_segment
225
-
226
- def initialize(pre_tokens: nil, post_tokens: nil, trigger: nil, custom_instructions: nil, preserved_segment: nil)
227
- @pre_tokens = pre_tokens
228
- @post_tokens = post_tokens
229
- @trigger = trigger # "manual" or "auto"
230
- @custom_instructions = custom_instructions
231
- @preserved_segment = preserved_segment # Hash with head_uuid, anchor_uuid, tail_uuid
232
- end
233
-
234
- def self.from_hash(hash)
235
- return nil unless hash.is_a?(Hash)
236
-
237
- preserved = hash[:preserved_segment] || hash['preserved_segment'] ||
238
- hash[:preservedSegment] || hash['preservedSegment']
239
-
240
- new(
241
- pre_tokens: hash[:pre_tokens] || hash['pre_tokens'] || hash[:preTokens] || hash['preTokens'],
242
- post_tokens: hash[:post_tokens] || hash['post_tokens'] || hash[:postTokens] || hash['postTokens'],
243
- trigger: hash[:trigger] || hash['trigger'],
244
- custom_instructions: hash[:custom_instructions] || hash['custom_instructions'] ||
245
- hash[:customInstructions] || hash['customInstructions'],
246
- preserved_segment: preserved
247
- )
248
- end
249
256
  end
250
257
 
251
258
  # Status system message (compacting status, permission mode changes)
252
259
  class StatusMessage < SystemMessage
253
260
  attr_accessor :uuid, :session_id, :status, :permission_mode
254
-
255
- def initialize(subtype:, data:, uuid: nil, session_id: nil, status: nil, permission_mode: nil)
256
- super(subtype: subtype, data: data)
257
- @uuid = uuid
258
- @session_id = session_id
259
- @status = status # "compacting" or nil
260
- @permission_mode = permission_mode
261
- end
262
261
  end
263
262
 
264
263
  # API retry system message
265
264
  class APIRetryMessage < SystemMessage
266
265
  attr_accessor :uuid, :session_id, :attempt, :max_retries, :retry_delay_ms, :error_status, :error
267
-
268
- def initialize(subtype:, data:, uuid: nil, session_id: nil, attempt: nil, max_retries: nil,
269
- retry_delay_ms: nil, error_status: nil, error: nil)
270
- super(subtype: subtype, data: data)
271
- @uuid = uuid
272
- @session_id = session_id
273
- @attempt = attempt
274
- @max_retries = max_retries
275
- @retry_delay_ms = retry_delay_ms
276
- @error_status = error_status
277
- @error = error
278
- end
279
266
  end
280
267
 
281
268
  # Local command output system message
282
269
  class LocalCommandOutputMessage < SystemMessage
283
270
  attr_accessor :uuid, :session_id, :content
284
-
285
- def initialize(subtype:, data:, uuid: nil, session_id: nil, content: nil)
286
- super(subtype: subtype, data: data)
287
- @uuid = uuid
288
- @session_id = session_id
289
- @content = content
290
- end
291
271
  end
292
272
 
293
273
  # Hook started system message
294
274
  class HookStartedMessage < SystemMessage
295
275
  attr_accessor :uuid, :session_id, :hook_id, :hook_name, :hook_event
296
-
297
- def initialize(subtype:, data:, uuid: nil, session_id: nil, hook_id: nil, hook_name: nil, hook_event: nil)
298
- super(subtype: subtype, data: data)
299
- @uuid = uuid
300
- @session_id = session_id
301
- @hook_id = hook_id
302
- @hook_name = hook_name
303
- @hook_event = hook_event
304
- end
305
276
  end
306
277
 
307
278
  # Hook progress system message
308
279
  class HookProgressMessage < SystemMessage
309
280
  attr_accessor :uuid, :session_id, :hook_id, :hook_name, :hook_event, :stdout, :stderr, :output
310
-
311
- def initialize(subtype:, data:, uuid: nil, session_id: nil, hook_id: nil, hook_name: nil,
312
- hook_event: nil, stdout: nil, stderr: nil, output: nil)
313
- super(subtype: subtype, data: data)
314
- @uuid = uuid
315
- @session_id = session_id
316
- @hook_id = hook_id
317
- @hook_name = hook_name
318
- @hook_event = hook_event
319
- @stdout = stdout
320
- @stderr = stderr
321
- @output = output
322
- end
323
281
  end
324
282
 
325
283
  # Hook response system message
326
284
  class HookResponseMessage < SystemMessage
327
285
  attr_accessor :uuid, :session_id, :hook_id, :hook_name, :hook_event,
328
- :output, :stdout, :stderr, :exit_code, :outcome
329
-
330
- def initialize(subtype:, data:, uuid: nil, session_id: nil, hook_id: nil, hook_name: nil,
331
- hook_event: nil, output: nil, stdout: nil, stderr: nil, exit_code: nil, outcome: nil)
332
- super(subtype: subtype, data: data)
333
- @uuid = uuid
334
- @session_id = session_id
335
- @hook_id = hook_id
336
- @hook_name = hook_name
337
- @hook_event = hook_event
338
- @output = output
339
- @stdout = stdout
340
- @stderr = stderr
341
- @exit_code = exit_code
342
- @outcome = outcome # "success", "error", or "cancelled"
343
- end
286
+ :output, :stdout, :stderr, :exit_code,
287
+ :outcome # "success", "error", or "cancelled"
344
288
  end
345
289
 
346
290
  # Session state changed system message
347
291
  class SessionStateChangedMessage < SystemMessage
348
- attr_accessor :uuid, :session_id, :state
349
-
350
- def initialize(subtype:, data:, uuid: nil, session_id: nil, state: nil)
351
- super(subtype: subtype, data: data)
352
- @uuid = uuid
353
- @session_id = session_id
354
- @state = state # "idle", "running", or "requires_action"
355
- end
292
+ attr_accessor :uuid, :session_id,
293
+ :state # "idle", "running", or "requires_action"
356
294
  end
357
295
 
358
296
  # Files persisted system message
359
297
  class FilesPersistedMessage < SystemMessage
360
298
  attr_accessor :uuid, :session_id, :files, :failed, :processed_at
361
-
362
- def initialize(subtype:, data:, uuid: nil, session_id: nil, files: nil, failed: nil, processed_at: nil)
363
- super(subtype: subtype, data: data)
364
- @uuid = uuid
365
- @session_id = session_id
366
- @files = files # Array of { filename:, file_id: }
367
- @failed = failed # Array of { filename:, error: }
368
- @processed_at = processed_at
369
- end
370
299
  end
371
300
 
372
301
  # Elicitation complete system message
373
302
  class ElicitationCompleteMessage < SystemMessage
374
303
  attr_accessor :uuid, :session_id, :mcp_server_name, :elicitation_id
375
-
376
- def initialize(subtype:, data:, uuid: nil, session_id: nil, mcp_server_name: nil, elicitation_id: nil)
377
- super(subtype: subtype, data: data)
378
- @uuid = uuid
379
- @session_id = session_id
380
- @mcp_server_name = mcp_server_name
381
- @elicitation_id = elicitation_id
382
- end
383
304
  end
384
305
 
385
306
  # Task lifecycle notification statuses
386
307
  TASK_NOTIFICATION_STATUSES = %w[completed failed stopped].freeze
387
308
 
388
309
  # Typed usage data for task progress and notifications
389
- class TaskUsage
310
+ class TaskUsage < Type
390
311
  attr_accessor :total_tokens, :tool_uses, :duration_ms
391
312
 
392
- def initialize(total_tokens: 0, tool_uses: 0, duration_ms: 0)
393
- @total_tokens = total_tokens
394
- @tool_uses = tool_uses
395
- @duration_ms = duration_ms
396
- end
397
-
398
- def self.from_hash(hash)
399
- return nil unless hash.is_a?(Hash)
400
-
401
- new(
402
- total_tokens: hash[:total_tokens] || hash['total_tokens'] || hash[:totalTokens] || hash['totalTokens'] || 0,
403
- tool_uses: hash[:tool_uses] || hash['tool_uses'] || hash[:toolUses] || hash['toolUses'] || 0,
404
- duration_ms: hash[:duration_ms] || hash['duration_ms'] || hash[:durationMs] || hash['durationMs'] || 0
405
- )
313
+ def initialize(attributes = {})
314
+ super
315
+ @total_tokens ||= 0
316
+ @tool_uses ||= 0
317
+ @duration_ms ||= 0
406
318
  end
407
319
  end
408
320
 
@@ -410,151 +322,54 @@ module ClaudeAgentSDK
410
322
  class TaskStartedMessage < SystemMessage
411
323
  attr_accessor :task_id, :description, :uuid, :session_id, :tool_use_id, :task_type,
412
324
  :workflow_name, :prompt
413
-
414
- def initialize(subtype:, data:, task_id:, description:, uuid:, session_id:,
415
- tool_use_id: nil, task_type: nil, workflow_name: nil, prompt: nil)
416
- super(subtype: subtype, data: data)
417
- @task_id = task_id
418
- @description = description
419
- @uuid = uuid
420
- @session_id = session_id
421
- @tool_use_id = tool_use_id
422
- @task_type = task_type
423
- @workflow_name = workflow_name
424
- @prompt = prompt
425
- end
426
325
  end
427
326
 
428
327
  # Task progress system message (periodic update from a running task)
429
328
  class TaskProgressMessage < SystemMessage
430
329
  attr_accessor :task_id, :description, :usage, :uuid, :session_id, :tool_use_id, :last_tool_name, :summary
431
-
432
- def initialize(subtype:, data:, task_id:, description:, usage:, uuid:, session_id:,
433
- tool_use_id: nil, last_tool_name: nil, summary: nil)
434
- super(subtype: subtype, data: data)
435
- @task_id = task_id
436
- @description = description
437
- @usage = usage
438
- @uuid = uuid
439
- @session_id = session_id
440
- @tool_use_id = tool_use_id
441
- @last_tool_name = last_tool_name
442
- @summary = summary
443
- end
444
330
  end
445
331
 
446
332
  # Task notification system message (task completed/failed/stopped)
447
333
  class TaskNotificationMessage < SystemMessage
448
334
  attr_accessor :task_id, :status, :output_file, :summary, :uuid, :session_id, :tool_use_id, :usage
449
-
450
- def initialize(subtype:, data:, task_id:, status:, output_file:, summary:, uuid:, session_id:,
451
- tool_use_id: nil, usage: nil)
452
- super(subtype: subtype, data: data)
453
- @task_id = task_id
454
- @status = status
455
- @output_file = output_file
456
- @summary = summary
457
- @uuid = uuid
458
- @session_id = session_id
459
- @tool_use_id = tool_use_id
460
- @usage = usage
461
- end
462
335
  end
463
336
 
464
337
  # Result message with cost and usage information
465
- class ResultMessage
338
+ class ResultMessage < Type
466
339
  attr_accessor :subtype, :duration_ms, :duration_api_ms, :is_error,
467
340
  :num_turns, :session_id, :stop_reason, :total_cost_usd, :usage,
468
- :result, :structured_output, :model_usage, :permission_denials, :errors,
469
- :uuid, :fast_mode_state
470
-
471
- def initialize(subtype:, duration_ms:, duration_api_ms:, is_error:,
472
- num_turns:, session_id:, stop_reason: nil, total_cost_usd: nil,
473
- usage: nil, result: nil, structured_output: nil,
474
- model_usage: nil, permission_denials: nil, errors: nil,
475
- uuid: nil, fast_mode_state: nil)
476
- @subtype = subtype
477
- @duration_ms = duration_ms
478
- @duration_api_ms = duration_api_ms
479
- @is_error = is_error
480
- @num_turns = num_turns
481
- @session_id = session_id
482
- @stop_reason = stop_reason
483
- @total_cost_usd = total_cost_usd
484
- @usage = usage
485
- @result = result
486
- @structured_output = structured_output
487
- @model_usage = model_usage # Hash of { model_name => usage_data }
488
- @permission_denials = permission_denials # Array of { tool_name:, tool_use_id:, tool_input: }
489
- @errors = errors # Array of error strings (present on error subtypes)
490
- @uuid = uuid
491
- @fast_mode_state = fast_mode_state # "off", "cooldown", or "on"
492
- end
341
+ :result, :structured_output,
342
+ :model_usage, # Hash of { model_name => usage_data }
343
+ :permission_denials, # Array of { tool_name:, tool_use_id:, tool_input: }
344
+ :errors, # Array of error strings (present on error subtypes)
345
+ :uuid,
346
+ :fast_mode_state # "off", "cooldown", or "on"
493
347
  end
494
348
 
495
349
  # Stream event for partial message updates
496
- class StreamEvent
350
+ class StreamEvent < Type
497
351
  attr_accessor :uuid, :session_id, :event, :parent_tool_use_id
498
-
499
- def initialize(uuid:, session_id:, event:, parent_tool_use_id: nil)
500
- @uuid = uuid
501
- @session_id = session_id
502
- @event = event
503
- @parent_tool_use_id = parent_tool_use_id
504
- end
505
352
  end
506
353
 
507
354
  # Tool progress message (type: 'tool_progress')
508
- class ToolProgressMessage
355
+ class ToolProgressMessage < Type
509
356
  attr_accessor :uuid, :session_id, :tool_use_id, :tool_name, :parent_tool_use_id,
510
357
  :elapsed_time_seconds, :task_id
511
-
512
- def initialize(uuid: nil, session_id: nil, tool_use_id: nil, tool_name: nil,
513
- parent_tool_use_id: nil, elapsed_time_seconds: nil, task_id: nil)
514
- @uuid = uuid
515
- @session_id = session_id
516
- @tool_use_id = tool_use_id
517
- @tool_name = tool_name
518
- @parent_tool_use_id = parent_tool_use_id
519
- @elapsed_time_seconds = elapsed_time_seconds
520
- @task_id = task_id
521
- end
522
358
  end
523
359
 
524
360
  # Auth status message (type: 'auth_status')
525
- class AuthStatusMessage
361
+ class AuthStatusMessage < Type
526
362
  attr_accessor :uuid, :session_id, :is_authenticating, :output, :error
527
-
528
- def initialize(uuid: nil, session_id: nil, is_authenticating: nil, output: nil, error: nil)
529
- @uuid = uuid
530
- @session_id = session_id
531
- @is_authenticating = is_authenticating
532
- @output = output
533
- @error = error
534
- end
535
363
  end
536
364
 
537
365
  # Tool use summary message (type: 'tool_use_summary')
538
- class ToolUseSummaryMessage
366
+ class ToolUseSummaryMessage < Type
539
367
  attr_accessor :uuid, :session_id, :summary, :preceding_tool_use_ids
540
-
541
- def initialize(uuid: nil, session_id: nil, summary: nil, preceding_tool_use_ids: nil)
542
- @uuid = uuid
543
- @session_id = session_id
544
- @summary = summary
545
- @preceding_tool_use_ids = preceding_tool_use_ids
546
- end
547
368
  end
548
369
 
549
370
  # Prompt suggestion message (type: 'prompt_suggestion')
550
- class PromptSuggestionMessage
371
+ class PromptSuggestionMessage < Type
551
372
  attr_accessor :uuid, :session_id, :suggestion
552
-
553
- def initialize(uuid: nil, session_id: nil, suggestion: nil)
554
- @uuid = uuid
555
- @session_id = session_id
556
- @suggestion = suggestion
557
- end
558
373
  end
559
374
 
560
375
  # Type constants for rate limit statuses
@@ -564,32 +379,28 @@ module ClaudeAgentSDK
564
379
  RATE_LIMIT_TYPES = %w[five_hour seven_day seven_day_opus seven_day_sonnet overage].freeze
565
380
 
566
381
  # Rate limit info with typed fields
567
- class RateLimitInfo
382
+ class RateLimitInfo < Type
568
383
  attr_accessor :status, :resets_at, :rate_limit_type, :utilization,
569
384
  :overage_status, :overage_resets_at, :overage_disabled_reason, :raw
570
385
 
571
- def initialize(status:, resets_at: nil, rate_limit_type: nil, utilization: nil,
572
- overage_status: nil, overage_resets_at: nil, overage_disabled_reason: nil, raw: {})
573
- @status = status
574
- @resets_at = resets_at
575
- @rate_limit_type = rate_limit_type
576
- @utilization = utilization
577
- @overage_status = overage_status
578
- @overage_resets_at = overage_resets_at
579
- @overage_disabled_reason = overage_disabled_reason
580
- @raw = raw
386
+ def initialize(attributes = {})
387
+ super
388
+ @raw ||= {}
581
389
  end
582
390
  end
583
391
 
584
392
  # Rate limit event emitted when rate limit info changes
585
- class RateLimitEvent
586
- attr_accessor :rate_limit_info, :uuid, :session_id
393
+ class RateLimitEvent < Type
394
+ attr_accessor :uuid, :session_id, :raw_data
395
+ attr_reader :rate_limit_info
396
+
397
+ def initialize(attributes = {})
398
+ super
399
+ @rate_limit_info ||= RateLimitInfo.new
400
+ end
587
401
 
588
- def initialize(rate_limit_info:, uuid:, session_id:, raw_data: nil)
589
- @rate_limit_info = rate_limit_info
590
- @uuid = uuid
591
- @session_id = session_id
592
- @raw_data = raw_data
402
+ def rate_limit_info=(value)
403
+ @rate_limit_info = value.is_a?(Hash) ? RateLimitInfo.new(value.merge(raw: value)) : value
593
404
  end
594
405
 
595
406
  # Backward-compatible accessor returning the full raw event payload
@@ -609,12 +420,16 @@ module ClaudeAgentSDK
609
420
  THINKING_DISPLAY_VALUES = %w[summarized omitted].freeze
610
421
 
611
422
  # Adaptive thinking: uses a default budget of 32000 tokens
612
- class ThinkingConfigAdaptive
613
- attr_accessor :type, :display
423
+ class ThinkingConfigAdaptive < Type
424
+ attr_reader :type, :display
614
425
 
615
- def initialize(display: nil)
426
+ def initialize(attributes = {})
427
+ super
616
428
  @type = 'adaptive'
617
- @display = validate_display(display)
429
+ end
430
+
431
+ def display=(value)
432
+ @display = validate_display(value)
618
433
  end
619
434
 
620
435
  private
@@ -629,13 +444,17 @@ module ClaudeAgentSDK
629
444
  end
630
445
 
631
446
  # Enabled thinking: uses a user-specified budget
632
- class ThinkingConfigEnabled
633
- attr_accessor :type, :budget_tokens, :display
447
+ class ThinkingConfigEnabled < Type
448
+ attr_accessor :budget_tokens
449
+ attr_reader :type, :display
634
450
 
635
- def initialize(budget_tokens:, display: nil)
451
+ def initialize(attributes = {})
452
+ super
636
453
  @type = 'enabled'
637
- @budget_tokens = budget_tokens
638
- @display = validate_display(display)
454
+ end
455
+
456
+ def display=(value)
457
+ @display = validate_display(value)
639
458
  end
640
459
 
641
460
  private
@@ -650,61 +469,30 @@ module ClaudeAgentSDK
650
469
  end
651
470
 
652
471
  # Disabled thinking: sets thinking tokens to 0
653
- class ThinkingConfigDisabled
654
- attr_accessor :type
472
+ class ThinkingConfigDisabled < Type
473
+ attr_reader :type
655
474
 
656
- def initialize
475
+ def initialize(attributes = {})
476
+ super
657
477
  @type = 'disabled'
658
478
  end
659
479
  end
660
480
 
661
481
  # Agent definition configuration
662
- class AgentDefinition
482
+ class AgentDefinition < Type
663
483
  attr_accessor :description, :prompt, :tools, :disallowed_tools, :model, :skills, :memory, :mcp_servers,
664
484
  :initial_prompt, :max_turns, :background, :effort, :permission_mode
665
-
666
- def initialize(description:, prompt:, tools: nil, disallowed_tools: nil, model: nil, skills: nil,
667
- memory: nil, mcp_servers: nil, initial_prompt: nil, max_turns: nil,
668
- background: nil, effort: nil, permission_mode: nil)
669
- @description = description
670
- @prompt = prompt
671
- @tools = tools
672
- @disallowed_tools = disallowed_tools # Array of tool names to disallow
673
- @model = model
674
- @skills = skills # Array of skill names
675
- @memory = memory # One of: 'user', 'project', 'local'
676
- @mcp_servers = mcp_servers # Array of server names or config hashes
677
- @initial_prompt = initial_prompt # Initial prompt sent when agent starts
678
- @max_turns = max_turns # Maximum conversation turns for the agent
679
- @background = background # Whether this agent runs in background
680
- @effort = effort # See ClaudeAgentSDK::EFFORT_LEVELS or an Integer
681
- @permission_mode = permission_mode # Permission mode for the agent
682
- end
683
485
  end
684
486
 
685
487
  # Permission rule value
686
- class PermissionRuleValue
488
+ class PermissionRuleValue < Type
687
489
  attr_accessor :tool_name, :rule_content
688
-
689
- def initialize(tool_name:, rule_content: nil)
690
- @tool_name = tool_name
691
- @rule_content = rule_content
692
- end
693
490
  end
694
491
 
695
492
  # Permission update configuration
696
- class PermissionUpdate
493
+ class PermissionUpdate < Type
697
494
  attr_accessor :type, :rules, :behavior, :mode, :directories, :destination
698
495
 
699
- def initialize(type:, rules: nil, behavior: nil, mode: nil, directories: nil, destination: nil)
700
- @type = type
701
- @rules = rules
702
- @behavior = behavior
703
- @mode = mode
704
- @directories = directories
705
- @destination = destination
706
- end
707
-
708
496
  def to_h
709
497
  result = { type: @type }
710
498
  result[:destination] = @destination if @destination
@@ -731,452 +519,342 @@ module ClaudeAgentSDK
731
519
  end
732
520
 
733
521
  # Tool permission context
734
- class ToolPermissionContext
522
+ class ToolPermissionContext < Type
735
523
  attr_accessor :signal, :suggestions, :tool_use_id, :agent_id
736
524
 
737
- def initialize(signal: nil, suggestions: [], tool_use_id: nil, agent_id: nil)
738
- @signal = signal
739
- @suggestions = suggestions
740
- @tool_use_id = tool_use_id # Unique ID for this tool call within the assistant message
741
- @agent_id = agent_id # Sub-agent ID if running within an agent context
525
+ def initialize(attributes = {})
526
+ super
527
+ @suggestions ||= []
742
528
  end
743
529
  end
744
530
 
745
531
  # Permission results
746
- class PermissionResultAllow
747
- attr_accessor :behavior, :updated_input, :updated_permissions
532
+ class PermissionResultAllow < Type
533
+ attr_accessor :updated_input, :updated_permissions
534
+ attr_reader :behavior
748
535
 
749
- def initialize(updated_input: nil, updated_permissions: nil)
536
+ def initialize(attributes = {})
537
+ super
750
538
  @behavior = 'allow'
751
- @updated_input = updated_input
752
- @updated_permissions = updated_permissions
753
539
  end
754
540
  end
755
541
 
756
- class PermissionResultDeny
757
- attr_accessor :behavior, :message, :interrupt
542
+ class PermissionResultDeny < Type
543
+ attr_accessor :message, :interrupt
544
+ attr_reader :behavior
758
545
 
759
- def initialize(message: '', interrupt: false)
546
+ def initialize(attributes = {})
547
+ super
760
548
  @behavior = 'deny'
761
- @message = message
762
- @interrupt = interrupt
549
+ @message ||= ''
550
+ @interrupt = false if @interrupt.nil?
763
551
  end
764
552
  end
765
553
 
766
554
  # Hook matcher configuration
767
- class HookMatcher
555
+ class HookMatcher < Type
768
556
  attr_accessor :matcher, :hooks, :timeout
769
557
 
770
- def initialize(matcher: nil, hooks: [], timeout: nil)
771
- @matcher = matcher
772
- @hooks = hooks
773
- @timeout = timeout # Timeout in seconds for hook execution
558
+ def initialize(attributes = {})
559
+ super
560
+ @hooks ||= []
774
561
  end
775
562
  end
776
563
 
777
564
  # Hook context passed to hook callbacks
778
- class HookContext
565
+ class HookContext < Type
779
566
  attr_accessor :signal
780
-
781
- def initialize(signal: nil)
782
- @signal = signal
783
- end
784
567
  end
785
568
 
786
569
  # Base hook input with common fields
787
- class BaseHookInput
570
+ class BaseHookInput < Type
788
571
  attr_accessor :session_id, :transcript_path, :cwd, :permission_mode
789
-
790
- def initialize(session_id: nil, transcript_path: nil, cwd: nil, permission_mode: nil)
791
- @session_id = session_id
792
- @transcript_path = transcript_path
793
- @cwd = cwd
794
- @permission_mode = permission_mode
795
- end
572
+ attr_reader :hook_event_name
796
573
  end
797
574
 
798
575
  # PreToolUse hook input
799
576
  class PreToolUseHookInput < BaseHookInput
800
- attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_use_id, :agent_id, :agent_type
577
+ attr_accessor :tool_name, :tool_input, :tool_use_id, :agent_id, :agent_type
801
578
 
802
- def initialize(hook_event_name: 'PreToolUse', tool_name: nil, tool_input: nil, tool_use_id: nil,
803
- agent_id: nil, agent_type: nil, **base_args)
804
- super(**base_args)
805
- @hook_event_name = hook_event_name
806
- @tool_name = tool_name
807
- @tool_input = tool_input
808
- @tool_use_id = tool_use_id
809
- @agent_id = agent_id
810
- @agent_type = agent_type
579
+ def initialize(attributes = {})
580
+ super
581
+ @hook_event_name = 'PreToolUse'
811
582
  end
812
583
  end
813
584
 
814
585
  # PostToolUse hook input
815
586
  class PostToolUseHookInput < BaseHookInput
816
- attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_response, :tool_use_id, :agent_id, :agent_type
587
+ attr_accessor :tool_name, :tool_input, :tool_response, :tool_use_id, :agent_id, :agent_type
817
588
 
818
- def initialize(hook_event_name: 'PostToolUse', tool_name: nil, tool_input: nil, tool_response: nil,
819
- tool_use_id: nil, agent_id: nil, agent_type: nil, **base_args)
820
- super(**base_args)
821
- @hook_event_name = hook_event_name
822
- @tool_name = tool_name
823
- @tool_input = tool_input
824
- @tool_response = tool_response
825
- @tool_use_id = tool_use_id
826
- @agent_id = agent_id
827
- @agent_type = agent_type
589
+ def initialize(attributes = {})
590
+ super
591
+ @hook_event_name = 'PostToolUse'
828
592
  end
829
593
  end
830
594
 
831
595
  # UserPromptSubmit hook input
832
596
  class UserPromptSubmitHookInput < BaseHookInput
833
- attr_accessor :hook_event_name, :prompt
597
+ attr_accessor :prompt
834
598
 
835
- def initialize(hook_event_name: 'UserPromptSubmit', prompt: nil, **base_args)
836
- super(**base_args)
837
- @hook_event_name = hook_event_name
838
- @prompt = prompt
599
+ def initialize(attributes = {})
600
+ super
601
+ @hook_event_name = 'UserPromptSubmit'
839
602
  end
840
603
  end
841
604
 
842
605
  # Stop hook input
843
606
  class StopHookInput < BaseHookInput
844
- attr_accessor :hook_event_name, :stop_hook_active, :last_assistant_message
607
+ attr_accessor :stop_hook_active, :last_assistant_message
845
608
 
846
- def initialize(hook_event_name: 'Stop', stop_hook_active: false, last_assistant_message: nil, **base_args)
847
- super(**base_args)
848
- @hook_event_name = hook_event_name
849
- @stop_hook_active = stop_hook_active
850
- @last_assistant_message = last_assistant_message
609
+ def initialize(attributes = {})
610
+ super
611
+ @hook_event_name = 'Stop'
612
+ @stop_hook_active = false if @stop_hook_active.nil?
851
613
  end
852
614
  end
853
615
 
854
616
  # SubagentStop hook input
855
617
  class SubagentStopHookInput < BaseHookInput
856
- attr_accessor :hook_event_name, :stop_hook_active, :agent_id, :agent_transcript_path, :agent_type,
618
+ attr_accessor :stop_hook_active, :agent_id, :agent_transcript_path, :agent_type,
857
619
  :last_assistant_message
858
620
 
859
- def initialize(hook_event_name: 'SubagentStop', stop_hook_active: false, agent_id: nil,
860
- agent_transcript_path: nil, agent_type: nil, last_assistant_message: nil, **base_args)
861
- super(**base_args)
862
- @hook_event_name = hook_event_name
863
- @stop_hook_active = stop_hook_active
864
- @agent_id = agent_id
865
- @agent_transcript_path = agent_transcript_path
866
- @agent_type = agent_type
867
- @last_assistant_message = last_assistant_message
621
+ def initialize(attributes = {})
622
+ super
623
+ @hook_event_name = 'SubagentStop'
624
+ @stop_hook_active = false if @stop_hook_active.nil?
868
625
  end
869
626
  end
870
627
 
871
628
  # PostToolUseFailure hook input
872
629
  class PostToolUseFailureHookInput < BaseHookInput
873
- attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_use_id, :error, :is_interrupt,
630
+ attr_accessor :tool_name, :tool_input, :tool_use_id, :error, :is_interrupt,
874
631
  :agent_id, :agent_type
875
632
 
876
- def initialize(hook_event_name: 'PostToolUseFailure', tool_name: nil, tool_input: nil, tool_use_id: nil,
877
- error: nil, is_interrupt: nil, agent_id: nil, agent_type: nil, **base_args)
878
- super(**base_args)
879
- @hook_event_name = hook_event_name
880
- @tool_name = tool_name
881
- @tool_input = tool_input
882
- @tool_use_id = tool_use_id
883
- @error = error
884
- @is_interrupt = is_interrupt
885
- @agent_id = agent_id
886
- @agent_type = agent_type
633
+ def initialize(attributes = {})
634
+ super
635
+ @hook_event_name = 'PostToolUseFailure'
887
636
  end
888
637
  end
889
638
 
890
639
  # Notification hook input
891
640
  class NotificationHookInput < BaseHookInput
892
- attr_accessor :hook_event_name, :message, :title, :notification_type
641
+ attr_accessor :message, :title, :notification_type
893
642
 
894
- def initialize(hook_event_name: 'Notification', message: nil, title: nil, notification_type: nil, **base_args)
895
- super(**base_args)
896
- @hook_event_name = hook_event_name
897
- @message = message
898
- @title = title
899
- @notification_type = notification_type
643
+ def initialize(attributes = {})
644
+ super
645
+ @hook_event_name = 'Notification'
900
646
  end
901
647
  end
902
648
 
903
649
  # SubagentStart hook input
904
650
  class SubagentStartHookInput < BaseHookInput
905
- attr_accessor :hook_event_name, :agent_id, :agent_type
651
+ attr_accessor :agent_id, :agent_type
906
652
 
907
- def initialize(hook_event_name: 'SubagentStart', agent_id: nil, agent_type: nil, **base_args)
908
- super(**base_args)
909
- @hook_event_name = hook_event_name
910
- @agent_id = agent_id
911
- @agent_type = agent_type
653
+ def initialize(attributes = {})
654
+ super
655
+ @hook_event_name = 'SubagentStart'
912
656
  end
913
657
  end
914
658
 
915
659
  # PermissionRequest hook input
916
660
  class PermissionRequestHookInput < BaseHookInput
917
- attr_accessor :hook_event_name, :tool_name, :tool_input, :permission_suggestions, :agent_id, :agent_type
661
+ attr_accessor :tool_name, :tool_input, :permission_suggestions, :agent_id, :agent_type
918
662
 
919
- def initialize(hook_event_name: 'PermissionRequest', tool_name: nil, tool_input: nil, permission_suggestions: nil,
920
- agent_id: nil, agent_type: nil, **base_args)
921
- super(**base_args)
922
- @hook_event_name = hook_event_name
923
- @tool_name = tool_name
924
- @tool_input = tool_input
925
- @permission_suggestions = permission_suggestions
926
- @agent_id = agent_id
927
- @agent_type = agent_type
663
+ def initialize(attributes = {})
664
+ super
665
+ @hook_event_name = 'PermissionRequest'
928
666
  end
929
667
  end
930
668
 
931
669
  # PreCompact hook input
932
670
  class PreCompactHookInput < BaseHookInput
933
- attr_accessor :hook_event_name, :trigger, :custom_instructions
671
+ attr_accessor :trigger, :custom_instructions
934
672
 
935
- def initialize(hook_event_name: 'PreCompact', trigger: nil, custom_instructions: nil, **base_args)
936
- super(**base_args)
937
- @hook_event_name = hook_event_name
938
- @trigger = trigger
939
- @custom_instructions = custom_instructions
673
+ def initialize(attributes = {})
674
+ super
675
+ @hook_event_name = 'PreCompact'
940
676
  end
941
677
  end
942
678
 
943
679
  # SessionStart hook input
944
680
  class SessionStartHookInput < BaseHookInput
945
- attr_accessor :hook_event_name, :source, :agent_type, :model
681
+ attr_accessor :source, :agent_type, :model
946
682
 
947
- def initialize(hook_event_name: 'SessionStart', source: nil, agent_type: nil, model: nil, **base_args)
948
- super(**base_args)
949
- @hook_event_name = hook_event_name
950
- @source = source # "startup", "resume", "clear", "compact"
951
- @agent_type = agent_type
952
- @model = model
683
+ def initialize(attributes = {})
684
+ super
685
+ @hook_event_name = 'SessionStart'
953
686
  end
954
687
  end
955
688
 
956
689
  # SessionEnd hook input
957
690
  class SessionEndHookInput < BaseHookInput
958
- attr_accessor :hook_event_name, :reason
691
+ attr_accessor :reason
959
692
 
960
- def initialize(hook_event_name: 'SessionEnd', reason: nil, **base_args)
961
- super(**base_args)
962
- @hook_event_name = hook_event_name
963
- @reason = reason
693
+ def initialize(attributes = {})
694
+ super
695
+ @hook_event_name = 'SessionEnd'
964
696
  end
965
697
  end
966
698
 
967
699
  # Setup hook input
968
700
  class SetupHookInput < BaseHookInput
969
- attr_accessor :hook_event_name, :trigger
701
+ attr_accessor :trigger
970
702
 
971
- def initialize(hook_event_name: 'Setup', trigger: nil, **base_args)
972
- super(**base_args)
973
- @hook_event_name = hook_event_name
974
- @trigger = trigger # "init" or "maintenance"
703
+ def initialize(attributes = {})
704
+ super
705
+ @hook_event_name = 'Setup'
975
706
  end
976
707
  end
977
708
 
978
709
  # TeammateIdle hook input
979
710
  class TeammateIdleHookInput < BaseHookInput
980
- attr_accessor :hook_event_name, :teammate_name, :team_name
711
+ attr_accessor :teammate_name, :team_name
981
712
 
982
- def initialize(hook_event_name: 'TeammateIdle', teammate_name: nil, team_name: nil, **base_args)
983
- super(**base_args)
984
- @hook_event_name = hook_event_name
985
- @teammate_name = teammate_name
986
- @team_name = team_name
713
+ def initialize(attributes = {})
714
+ super
715
+ @hook_event_name = 'TeammateIdle'
987
716
  end
988
717
  end
989
718
 
990
719
  # TaskCompleted hook input
991
720
  class TaskCompletedHookInput < BaseHookInput
992
- attr_accessor :hook_event_name, :task_id, :task_subject, :task_description, :teammate_name, :team_name
721
+ attr_accessor :task_id, :task_subject, :task_description, :teammate_name, :team_name
993
722
 
994
- def initialize(hook_event_name: 'TaskCompleted', task_id: nil, task_subject: nil, task_description: nil,
995
- teammate_name: nil, team_name: nil, **base_args)
996
- super(**base_args)
997
- @hook_event_name = hook_event_name
998
- @task_id = task_id
999
- @task_subject = task_subject
1000
- @task_description = task_description
1001
- @teammate_name = teammate_name
1002
- @team_name = team_name
723
+ def initialize(attributes = {})
724
+ super
725
+ @hook_event_name = 'TaskCompleted'
1003
726
  end
1004
727
  end
1005
728
 
1006
729
  # ConfigChange hook input
1007
730
  class ConfigChangeHookInput < BaseHookInput
1008
- attr_accessor :hook_event_name, :source, :file_path
731
+ attr_accessor :source, :file_path
1009
732
 
1010
- def initialize(hook_event_name: 'ConfigChange', source: nil, file_path: nil, **base_args)
1011
- super(**base_args)
1012
- @hook_event_name = hook_event_name
1013
- @source = source # "user_settings", "project_settings", "local_settings", "policy_settings", "skills"
1014
- @file_path = file_path
733
+ def initialize(attributes = {})
734
+ super
735
+ @hook_event_name = 'ConfigChange'
1015
736
  end
1016
737
  end
1017
738
 
1018
739
  # WorktreeCreate hook input
1019
740
  class WorktreeCreateHookInput < BaseHookInput
1020
- attr_accessor :hook_event_name, :name
741
+ attr_accessor :name
1021
742
 
1022
- def initialize(hook_event_name: 'WorktreeCreate', name: nil, **base_args)
1023
- super(**base_args)
1024
- @hook_event_name = hook_event_name
1025
- @name = name
743
+ def initialize(attributes = {})
744
+ super
745
+ @hook_event_name = 'WorktreeCreate'
1026
746
  end
1027
747
  end
1028
748
 
1029
749
  # WorktreeRemove hook input
1030
750
  class WorktreeRemoveHookInput < BaseHookInput
1031
- attr_accessor :hook_event_name, :worktree_path
751
+ attr_accessor :worktree_path
1032
752
 
1033
- def initialize(hook_event_name: 'WorktreeRemove', worktree_path: nil, **base_args)
1034
- super(**base_args)
1035
- @hook_event_name = hook_event_name
1036
- @worktree_path = worktree_path
753
+ def initialize(attributes = {})
754
+ super
755
+ @hook_event_name = 'WorktreeRemove'
1037
756
  end
1038
757
  end
1039
758
 
1040
759
  # StopFailure hook input
1041
760
  class StopFailureHookInput < BaseHookInput
1042
- attr_accessor :hook_event_name, :error, :error_details, :last_assistant_message
761
+ attr_accessor :error, :error_details, :last_assistant_message
1043
762
 
1044
- def initialize(hook_event_name: 'StopFailure', error: nil, error_details: nil,
1045
- last_assistant_message: nil, **base_args)
1046
- super(**base_args)
1047
- @hook_event_name = hook_event_name
1048
- @error = error
1049
- @error_details = error_details
1050
- @last_assistant_message = last_assistant_message
763
+ def initialize(attributes = {})
764
+ super
765
+ @hook_event_name = 'StopFailure'
1051
766
  end
1052
767
  end
1053
768
 
1054
769
  # PostCompact hook input
1055
770
  class PostCompactHookInput < BaseHookInput
1056
- attr_accessor :hook_event_name, :trigger, :compact_summary
771
+ attr_accessor :trigger, :compact_summary
1057
772
 
1058
- def initialize(hook_event_name: 'PostCompact', trigger: nil, compact_summary: nil, **base_args)
1059
- super(**base_args)
1060
- @hook_event_name = hook_event_name
1061
- @trigger = trigger # "manual" or "auto"
1062
- @compact_summary = compact_summary
773
+ def initialize(attributes = {})
774
+ super
775
+ @hook_event_name = 'PostCompact'
1063
776
  end
1064
777
  end
1065
778
 
1066
779
  # PermissionDenied hook input
1067
780
  class PermissionDeniedHookInput < BaseHookInput
1068
- attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_use_id, :reason, :agent_id, :agent_type
781
+ attr_accessor :tool_name, :tool_input, :tool_use_id, :reason, :agent_id, :agent_type
1069
782
 
1070
- def initialize(hook_event_name: 'PermissionDenied', tool_name: nil, tool_input: nil, tool_use_id: nil,
1071
- reason: nil, agent_id: nil, agent_type: nil, **base_args)
1072
- super(**base_args)
1073
- @hook_event_name = hook_event_name
1074
- @tool_name = tool_name
1075
- @tool_input = tool_input
1076
- @tool_use_id = tool_use_id
1077
- @reason = reason
1078
- @agent_id = agent_id
1079
- @agent_type = agent_type
783
+ def initialize(attributes = {})
784
+ super
785
+ @hook_event_name = 'PermissionDenied'
1080
786
  end
1081
787
  end
1082
788
 
1083
789
  # TaskCreated hook input
1084
790
  class TaskCreatedHookInput < BaseHookInput
1085
- attr_accessor :hook_event_name, :task_id, :task_subject, :task_description, :teammate_name, :team_name
791
+ attr_accessor :task_id, :task_subject, :task_description, :teammate_name, :team_name
1086
792
 
1087
- def initialize(hook_event_name: 'TaskCreated', task_id: nil, task_subject: nil, task_description: nil,
1088
- teammate_name: nil, team_name: nil, **base_args)
1089
- super(**base_args)
1090
- @hook_event_name = hook_event_name
1091
- @task_id = task_id
1092
- @task_subject = task_subject
1093
- @task_description = task_description
1094
- @teammate_name = teammate_name
1095
- @team_name = team_name
793
+ def initialize(attributes = {})
794
+ super
795
+ @hook_event_name = 'TaskCreated'
1096
796
  end
1097
797
  end
1098
798
 
1099
799
  # Elicitation hook input
1100
800
  class ElicitationHookInput < BaseHookInput
1101
- attr_accessor :hook_event_name, :mcp_server_name, :message, :mode, :url,
801
+ attr_accessor :mcp_server_name, :message, :mode, :url,
1102
802
  :elicitation_id, :requested_schema
1103
803
 
1104
- def initialize(hook_event_name: 'Elicitation', mcp_server_name: nil, message: nil, mode: nil,
1105
- url: nil, elicitation_id: nil, requested_schema: nil, **base_args)
1106
- super(**base_args)
1107
- @hook_event_name = hook_event_name
1108
- @mcp_server_name = mcp_server_name
1109
- @message = message
1110
- @mode = mode
1111
- @url = url
1112
- @elicitation_id = elicitation_id
1113
- @requested_schema = requested_schema
804
+ def initialize(attributes = {})
805
+ super
806
+ @hook_event_name = 'Elicitation'
1114
807
  end
1115
808
  end
1116
809
 
1117
810
  # ElicitationResult hook input
1118
811
  class ElicitationResultHookInput < BaseHookInput
1119
- attr_accessor :hook_event_name, :mcp_server_name, :elicitation_id, :mode, :action, :content
812
+ attr_accessor :mcp_server_name, :elicitation_id, :mode, :action, :content
1120
813
 
1121
- def initialize(hook_event_name: 'ElicitationResult', mcp_server_name: nil, elicitation_id: nil,
1122
- mode: nil, action: nil, content: nil, **base_args)
1123
- super(**base_args)
1124
- @hook_event_name = hook_event_name
1125
- @mcp_server_name = mcp_server_name
1126
- @elicitation_id = elicitation_id
1127
- @mode = mode
1128
- @action = action
1129
- @content = content
814
+ def initialize(attributes = {})
815
+ super
816
+ @hook_event_name = 'ElicitationResult'
1130
817
  end
1131
818
  end
1132
819
 
1133
820
  # InstructionsLoaded hook input
1134
821
  class InstructionsLoadedHookInput < BaseHookInput
1135
- attr_accessor :hook_event_name, :file_path, :memory_type, :load_reason, :globs, :trigger_file_path
822
+ attr_accessor :file_path, :memory_type, :load_reason, :globs, :trigger_file_path
1136
823
 
1137
- def initialize(hook_event_name: 'InstructionsLoaded', file_path: nil, memory_type: nil,
1138
- load_reason: nil, globs: nil, trigger_file_path: nil, **base_args)
1139
- super(**base_args)
1140
- @hook_event_name = hook_event_name
1141
- @file_path = file_path
1142
- @memory_type = memory_type
1143
- @load_reason = load_reason
1144
- @globs = globs
1145
- @trigger_file_path = trigger_file_path
824
+ def initialize(attributes = {})
825
+ super
826
+ @hook_event_name = 'InstructionsLoaded'
1146
827
  end
1147
828
  end
1148
829
 
1149
830
  # CwdChanged hook input
1150
831
  class CwdChangedHookInput < BaseHookInput
1151
- attr_accessor :hook_event_name, :old_cwd, :new_cwd
832
+ attr_accessor :old_cwd, :new_cwd
1152
833
 
1153
- def initialize(hook_event_name: 'CwdChanged', old_cwd: nil, new_cwd: nil, **base_args)
1154
- super(**base_args)
1155
- @hook_event_name = hook_event_name
1156
- @old_cwd = old_cwd
1157
- @new_cwd = new_cwd
834
+ def initialize(attributes = {})
835
+ super
836
+ @hook_event_name = 'CwdChanged'
1158
837
  end
1159
838
  end
1160
839
 
1161
840
  # FileChanged hook input
1162
841
  class FileChangedHookInput < BaseHookInput
1163
- attr_accessor :hook_event_name, :file_path, :event
842
+ attr_accessor :file_path, :event
1164
843
 
1165
- def initialize(hook_event_name: 'FileChanged', file_path: nil, event: nil, **base_args)
1166
- super(**base_args)
1167
- @hook_event_name = hook_event_name
1168
- @file_path = file_path
1169
- @event = event # "change", "add", or "unlink"
844
+ def initialize(attributes = {})
845
+ super
846
+ @hook_event_name = 'FileChanged'
1170
847
  end
1171
848
  end
1172
849
 
1173
850
  # Setup hook specific output
1174
- class SetupHookSpecificOutput
1175
- attr_accessor :hook_event_name, :additional_context
851
+ class SetupHookSpecificOutput < Type
852
+ attr_accessor :additional_context
853
+ attr_reader :hook_event_name
1176
854
 
1177
- def initialize(additional_context: nil)
855
+ def initialize(attributes = {})
856
+ super
1178
857
  @hook_event_name = 'Setup'
1179
- @additional_context = additional_context
1180
858
  end
1181
859
 
1182
860
  def to_h
@@ -1187,17 +865,14 @@ module ClaudeAgentSDK
1187
865
  end
1188
866
 
1189
867
  # PreToolUse hook specific output
1190
- class PreToolUseHookSpecificOutput
1191
- attr_accessor :hook_event_name, :permission_decision, :permission_decision_reason,
868
+ class PreToolUseHookSpecificOutput < Type
869
+ attr_accessor :permission_decision, :permission_decision_reason,
1192
870
  :updated_input, :additional_context
871
+ attr_reader :hook_event_name
1193
872
 
1194
- def initialize(permission_decision: nil, permission_decision_reason: nil, updated_input: nil,
1195
- additional_context: nil)
873
+ def initialize(attributes = {})
874
+ super
1196
875
  @hook_event_name = 'PreToolUse'
1197
- @permission_decision = permission_decision # 'allow', 'deny', or 'ask'
1198
- @permission_decision_reason = permission_decision_reason
1199
- @updated_input = updated_input
1200
- @additional_context = additional_context
1201
876
  end
1202
877
 
1203
878
  def to_h
@@ -1211,13 +886,13 @@ module ClaudeAgentSDK
1211
886
  end
1212
887
 
1213
888
  # PostToolUse hook specific output
1214
- class PostToolUseHookSpecificOutput
1215
- attr_accessor :hook_event_name, :additional_context, :updated_mcp_tool_output
889
+ class PostToolUseHookSpecificOutput < Type
890
+ attr_accessor :additional_context, :updated_mcp_tool_output
891
+ attr_reader :hook_event_name
1216
892
 
1217
- def initialize(additional_context: nil, updated_mcp_tool_output: nil)
893
+ def initialize(attributes = {})
894
+ super
1218
895
  @hook_event_name = 'PostToolUse'
1219
- @additional_context = additional_context
1220
- @updated_mcp_tool_output = updated_mcp_tool_output
1221
896
  end
1222
897
 
1223
898
  def to_h
@@ -1229,12 +904,13 @@ module ClaudeAgentSDK
1229
904
  end
1230
905
 
1231
906
  # PostToolUseFailure hook specific output
1232
- class PostToolUseFailureHookSpecificOutput
1233
- attr_accessor :hook_event_name, :additional_context
907
+ class PostToolUseFailureHookSpecificOutput < Type
908
+ attr_accessor :additional_context
909
+ attr_reader :hook_event_name
1234
910
 
1235
- def initialize(additional_context: nil)
911
+ def initialize(attributes = {})
912
+ super
1236
913
  @hook_event_name = 'PostToolUseFailure'
1237
- @additional_context = additional_context
1238
914
  end
1239
915
 
1240
916
  def to_h
@@ -1245,12 +921,13 @@ module ClaudeAgentSDK
1245
921
  end
1246
922
 
1247
923
  # UserPromptSubmit hook specific output
1248
- class UserPromptSubmitHookSpecificOutput
1249
- attr_accessor :hook_event_name, :additional_context
924
+ class UserPromptSubmitHookSpecificOutput < Type
925
+ attr_accessor :additional_context
926
+ attr_reader :hook_event_name
1250
927
 
1251
- def initialize(additional_context: nil)
928
+ def initialize(attributes = {})
929
+ super
1252
930
  @hook_event_name = 'UserPromptSubmit'
1253
- @additional_context = additional_context
1254
931
  end
1255
932
 
1256
933
  def to_h
@@ -1261,12 +938,13 @@ module ClaudeAgentSDK
1261
938
  end
1262
939
 
1263
940
  # Notification hook specific output
1264
- class NotificationHookSpecificOutput
1265
- attr_accessor :hook_event_name, :additional_context
941
+ class NotificationHookSpecificOutput < Type
942
+ attr_accessor :additional_context
943
+ attr_reader :hook_event_name
1266
944
 
1267
- def initialize(additional_context: nil)
945
+ def initialize(attributes = {})
946
+ super
1268
947
  @hook_event_name = 'Notification'
1269
- @additional_context = additional_context
1270
948
  end
1271
949
 
1272
950
  def to_h
@@ -1277,12 +955,13 @@ module ClaudeAgentSDK
1277
955
  end
1278
956
 
1279
957
  # SubagentStart hook specific output
1280
- class SubagentStartHookSpecificOutput
1281
- attr_accessor :hook_event_name, :additional_context
958
+ class SubagentStartHookSpecificOutput < Type
959
+ attr_accessor :additional_context
960
+ attr_reader :hook_event_name
1282
961
 
1283
- def initialize(additional_context: nil)
962
+ def initialize(attributes = {})
963
+ super
1284
964
  @hook_event_name = 'SubagentStart'
1285
- @additional_context = additional_context
1286
965
  end
1287
966
 
1288
967
  def to_h
@@ -1293,12 +972,13 @@ module ClaudeAgentSDK
1293
972
  end
1294
973
 
1295
974
  # PermissionRequest hook specific output
1296
- class PermissionRequestHookSpecificOutput
1297
- attr_accessor :hook_event_name, :decision
975
+ class PermissionRequestHookSpecificOutput < Type
976
+ attr_accessor :decision
977
+ attr_reader :hook_event_name
1298
978
 
1299
- def initialize(decision: nil)
979
+ def initialize(attributes = {})
980
+ super
1300
981
  @hook_event_name = 'PermissionRequest'
1301
- @decision = decision
1302
982
  end
1303
983
 
1304
984
  def to_h
@@ -1309,12 +989,13 @@ module ClaudeAgentSDK
1309
989
  end
1310
990
 
1311
991
  # SessionStart hook specific output
1312
- class SessionStartHookSpecificOutput
1313
- attr_accessor :hook_event_name, :additional_context
992
+ class SessionStartHookSpecificOutput < Type
993
+ attr_accessor :additional_context
994
+ attr_reader :hook_event_name
1314
995
 
1315
- def initialize(additional_context: nil)
996
+ def initialize(attributes = {})
997
+ super
1316
998
  @hook_event_name = 'SessionStart'
1317
- @additional_context = additional_context
1318
999
  end
1319
1000
 
1320
1001
  def to_h
@@ -1325,12 +1006,14 @@ module ClaudeAgentSDK
1325
1006
  end
1326
1007
 
1327
1008
  # PermissionDenied hook specific output
1328
- class PermissionDeniedHookSpecificOutput
1329
- attr_accessor :hook_event_name, :retry
1009
+ class PermissionDeniedHookSpecificOutput < Type
1010
+ attr_accessor :retry
1011
+ attr_reader :hook_event_name
1330
1012
 
1331
- def initialize(retry_: false)
1013
+ def initialize(attributes = {})
1014
+ super
1332
1015
  @hook_event_name = 'PermissionDenied'
1333
- @retry = retry_
1016
+ @retry = false if @retry.nil?
1334
1017
  end
1335
1018
 
1336
1019
  def to_h
@@ -1341,12 +1024,13 @@ module ClaudeAgentSDK
1341
1024
  end
1342
1025
 
1343
1026
  # CwdChanged hook specific output
1344
- class CwdChangedHookSpecificOutput
1345
- attr_accessor :hook_event_name, :watch_paths
1027
+ class CwdChangedHookSpecificOutput < Type
1028
+ attr_accessor :watch_paths
1029
+ attr_reader :hook_event_name
1346
1030
 
1347
- def initialize(watch_paths: nil)
1031
+ def initialize(attributes = {})
1032
+ super
1348
1033
  @hook_event_name = 'CwdChanged'
1349
- @watch_paths = watch_paths
1350
1034
  end
1351
1035
 
1352
1036
  def to_h
@@ -1357,12 +1041,13 @@ module ClaudeAgentSDK
1357
1041
  end
1358
1042
 
1359
1043
  # FileChanged hook specific output
1360
- class FileChangedHookSpecificOutput
1361
- attr_accessor :hook_event_name, :watch_paths
1044
+ class FileChangedHookSpecificOutput < Type
1045
+ attr_accessor :watch_paths
1046
+ attr_reader :hook_event_name
1362
1047
 
1363
- def initialize(watch_paths: nil)
1048
+ def initialize(attributes = {})
1049
+ super
1364
1050
  @hook_event_name = 'FileChanged'
1365
- @watch_paths = watch_paths
1366
1051
  end
1367
1052
 
1368
1053
  def to_h
@@ -1373,12 +1058,12 @@ module ClaudeAgentSDK
1373
1058
  end
1374
1059
 
1375
1060
  # Async hook JSON output
1376
- class AsyncHookJSONOutput
1061
+ class AsyncHookJSONOutput < Type
1377
1062
  attr_accessor :async, :async_timeout
1378
1063
 
1379
- def initialize(async: true, async_timeout: nil)
1380
- @async = async
1381
- @async_timeout = async_timeout
1064
+ def initialize(attributes = {})
1065
+ super
1066
+ @async = true if @async.nil?
1382
1067
  end
1383
1068
 
1384
1069
  def to_h
@@ -1389,19 +1074,14 @@ module ClaudeAgentSDK
1389
1074
  end
1390
1075
 
1391
1076
  # Sync hook JSON output
1392
- class SyncHookJSONOutput
1077
+ class SyncHookJSONOutput < Type
1393
1078
  attr_accessor :continue, :suppress_output, :stop_reason, :decision,
1394
1079
  :system_message, :reason, :hook_specific_output
1395
1080
 
1396
- def initialize(continue: true, suppress_output: false, stop_reason: nil, decision: nil,
1397
- system_message: nil, reason: nil, hook_specific_output: nil)
1398
- @continue = continue
1399
- @suppress_output = suppress_output
1400
- @stop_reason = stop_reason
1401
- @decision = decision
1402
- @system_message = system_message
1403
- @reason = reason
1404
- @hook_specific_output = hook_specific_output
1081
+ def initialize(attributes = {})
1082
+ super
1083
+ @continue = true if @continue.nil?
1084
+ @suppress_output = false if @suppress_output.nil?
1405
1085
  end
1406
1086
 
1407
1087
  def to_h
@@ -1422,63 +1102,44 @@ module ClaudeAgentSDK
1422
1102
  MCP_SERVER_CONNECTION_STATUSES = %w[connected failed needs-auth pending disabled].freeze
1423
1103
 
1424
1104
  # MCP server info (name and version)
1425
- class McpServerInfo
1105
+ class McpServerInfo < Type
1426
1106
  attr_accessor :name, :version
1427
-
1428
- def initialize(name:, version: nil)
1429
- @name = name
1430
- @version = version
1431
- end
1432
1107
  end
1433
1108
 
1434
1109
  # MCP tool annotation hints
1435
- class McpToolAnnotations
1110
+ class McpToolAnnotations < Type
1436
1111
  attr_accessor :read_only, :destructive, :open_world
1437
1112
 
1438
- def initialize(read_only: nil, destructive: nil, open_world: nil)
1439
- @read_only = read_only
1440
- @destructive = destructive
1441
- @open_world = open_world
1442
- end
1443
-
1113
+ # Backwards-compatible parse; returns nil for nil input.
1444
1114
  def self.parse(data)
1445
- return nil unless data
1446
-
1447
- new(
1448
- read_only: data.key?(:readOnly) ? data[:readOnly] : data[:read_only],
1449
- destructive: data[:destructive],
1450
- open_world: data.key?(:openWorld) ? data[:openWorld] : data[:open_world]
1451
- )
1115
+ from_hash(data)
1452
1116
  end
1453
1117
  end
1454
1118
 
1455
1119
  # MCP tool info (name, description, annotations)
1456
- class McpToolInfo
1457
- attr_accessor :name, :description, :annotations
1120
+ class McpToolInfo < Type
1121
+ attr_accessor :name, :description
1122
+ attr_reader :annotations
1458
1123
 
1459
- def initialize(name:, description: nil, annotations: nil)
1460
- @name = name
1461
- @description = description
1462
- @annotations = annotations
1124
+ def annotations=(value)
1125
+ @annotations = value.is_a?(Hash) ? McpToolAnnotations.new(value) : value
1463
1126
  end
1464
1127
 
1128
+ # Backwards-compatible parse; returns nil for nil input.
1465
1129
  def self.parse(data)
1466
- new(
1467
- name: data[:name],
1468
- description: data[:description],
1469
- annotations: McpToolAnnotations.parse(data[:annotations])
1470
- )
1130
+ from_hash(data)
1471
1131
  end
1472
1132
  end
1473
1133
 
1474
1134
  # Output-only serializable version of McpSdkServerConfig (without live instance)
1475
1135
  # Returned in MCP status responses
1476
- class McpSdkServerConfigStatus
1477
- attr_accessor :type, :name
1136
+ class McpSdkServerConfigStatus < Type
1137
+ attr_accessor :name
1138
+ attr_reader :type
1478
1139
 
1479
- def initialize(type: 'sdk', name:)
1480
- @type = type
1481
- @name = name
1140
+ def initialize(attributes = {})
1141
+ super
1142
+ @type = 'sdk'
1482
1143
  end
1483
1144
 
1484
1145
  def to_h
@@ -1488,13 +1149,13 @@ module ClaudeAgentSDK
1488
1149
 
1489
1150
  # Claude.ai proxy MCP server config
1490
1151
  # Output-only type that appears in status responses for servers proxied through Claude.ai
1491
- class McpClaudeAIProxyServerConfig
1492
- attr_accessor :type, :url, :id
1152
+ class McpClaudeAIProxyServerConfig < Type
1153
+ attr_accessor :url, :id
1154
+ attr_reader :type
1493
1155
 
1494
- def initialize(type: 'claudeai-proxy', url:, id:)
1495
- @type = type
1496
- @url = url
1497
- @id = id
1156
+ def initialize(attributes = {})
1157
+ super
1158
+ @type = 'claudeai-proxy'
1498
1159
  end
1499
1160
 
1500
1161
  def to_h
@@ -1503,37 +1164,34 @@ module ClaudeAgentSDK
1503
1164
  end
1504
1165
 
1505
1166
  # Status of a single MCP server connection
1506
- class McpServerStatus
1507
- attr_accessor :name, :status, :server_info, :error, :config, :scope, :tools
1167
+ class McpServerStatus < Type
1168
+ attr_accessor :name, :status, :error, :scope
1169
+ attr_reader :server_info, :config, :tools
1508
1170
 
1509
- def initialize(name:, status:, server_info: nil, error: nil, config: nil, scope: nil, tools: nil)
1510
- @name = name
1511
- @status = status
1512
- @server_info = server_info
1513
- @error = error
1514
- @config = config
1515
- @scope = scope
1516
- @tools = tools
1171
+ def server_info=(value)
1172
+ @server_info = value.is_a?(Hash) ? McpServerInfo.new(value) : value
1517
1173
  end
1518
1174
 
1519
- def self.parse(data)
1520
- server_info = (McpServerInfo.new(name: data[:serverInfo][:name], version: data[:serverInfo][:version]) if data[:serverInfo])
1521
- tools = data[:tools]&.map { |t| McpToolInfo.parse(t) }
1522
- config = parse_config(data[:config])
1175
+ def tools=(value)
1176
+ @tools = if value.is_a?(Array)
1177
+ value.map { |t| t.is_a?(Hash) ? McpToolInfo.new(t) : t }
1178
+ else
1179
+ value
1180
+ end
1181
+ end
1182
+
1183
+ def config=(value)
1184
+ @config = self.class.parse_config(value) || value
1185
+ end
1523
1186
 
1524
- new(
1525
- name: data[:name],
1526
- status: data[:status],
1527
- server_info: server_info,
1528
- error: data[:error],
1529
- config: config,
1530
- scope: data[:scope],
1531
- tools: tools
1532
- )
1187
+ # Backwards-compatible parse; normalizes camelCase `serverInfo` and
1188
+ # polymorphically builds the nested `config`.
1189
+ def self.parse(data)
1190
+ from_hash(data)
1533
1191
  end
1534
1192
 
1535
1193
  def self.parse_config(config)
1536
- return config unless config.is_a?(Hash) && config[:type]
1194
+ return nil unless config.is_a?(Hash) && config[:type]
1537
1195
 
1538
1196
  case config[:type]
1539
1197
  when 'claudeai-proxy'
@@ -1547,28 +1205,31 @@ module ClaudeAgentSDK
1547
1205
  end
1548
1206
 
1549
1207
  # Response from get_mcp_status containing all server statuses
1550
- class McpStatusResponse
1551
- attr_accessor :mcp_servers
1208
+ class McpStatusResponse < Type
1209
+ attr_reader :mcp_servers
1552
1210
 
1553
- def initialize(mcp_servers:)
1554
- @mcp_servers = mcp_servers
1211
+ def mcp_servers=(value)
1212
+ @mcp_servers = if value.is_a?(Array)
1213
+ value.map { |s| s.is_a?(Hash) ? McpServerStatus.new(s) : s }
1214
+ else
1215
+ value
1216
+ end
1555
1217
  end
1556
1218
 
1219
+ # Backwards-compatible parse; returns nil for nil input.
1557
1220
  def self.parse(data)
1558
- servers = (data[:mcpServers] || []).map { |s| McpServerStatus.parse(s) }
1559
- new(mcp_servers: servers)
1221
+ from_hash(data)
1560
1222
  end
1561
1223
  end
1562
1224
 
1563
1225
  # MCP Server configurations
1564
- class McpStdioServerConfig
1565
- attr_accessor :type, :command, :args, :env
1226
+ class McpStdioServerConfig < Type
1227
+ attr_accessor :command, :args, :env
1228
+ attr_reader :type
1566
1229
 
1567
- def initialize(command:, args: nil, env: nil, type: 'stdio')
1568
- @type = type
1569
- @command = command
1570
- @args = args
1571
- @env = env
1230
+ def initialize(attributes = {})
1231
+ super
1232
+ @type = 'stdio'
1572
1233
  end
1573
1234
 
1574
1235
  def to_h
@@ -1579,13 +1240,13 @@ module ClaudeAgentSDK
1579
1240
  end
1580
1241
  end
1581
1242
 
1582
- class McpSSEServerConfig
1583
- attr_accessor :type, :url, :headers
1243
+ class McpSSEServerConfig < Type
1244
+ attr_accessor :url, :headers
1245
+ attr_reader :type
1584
1246
 
1585
- def initialize(url:, headers: nil)
1247
+ def initialize(attributes = {})
1248
+ super
1586
1249
  @type = 'sse'
1587
- @url = url
1588
- @headers = headers
1589
1250
  end
1590
1251
 
1591
1252
  def to_h
@@ -1595,13 +1256,13 @@ module ClaudeAgentSDK
1595
1256
  end
1596
1257
  end
1597
1258
 
1598
- class McpHttpServerConfig
1599
- attr_accessor :type, :url, :headers
1259
+ class McpHttpServerConfig < Type
1260
+ attr_accessor :url, :headers
1261
+ attr_reader :type
1600
1262
 
1601
- def initialize(url:, headers: nil)
1263
+ def initialize(attributes = {})
1264
+ super
1602
1265
  @type = 'http'
1603
- @url = url
1604
- @headers = headers
1605
1266
  end
1606
1267
 
1607
1268
  def to_h
@@ -1611,13 +1272,13 @@ module ClaudeAgentSDK
1611
1272
  end
1612
1273
  end
1613
1274
 
1614
- class McpSdkServerConfig
1615
- attr_accessor :type, :name, :instance
1275
+ class McpSdkServerConfig < Type
1276
+ attr_accessor :name, :instance
1277
+ attr_reader :type
1616
1278
 
1617
- def initialize(name:, instance:)
1279
+ def initialize(attributes = {})
1280
+ super
1618
1281
  @type = 'sdk'
1619
- @name = name
1620
- @instance = instance
1621
1282
  end
1622
1283
 
1623
1284
  def to_h
@@ -1626,14 +1287,13 @@ module ClaudeAgentSDK
1626
1287
  end
1627
1288
 
1628
1289
  # SDK Plugin configuration
1629
- class SdkPluginConfig
1630
- attr_accessor :type, :path
1631
-
1632
- def initialize(path:, type: 'local')
1633
- raise ArgumentError, "unsupported plugin type: #{type}" unless %w[local plugin].include?(type)
1290
+ class SdkPluginConfig < Type
1291
+ attr_accessor :path
1292
+ attr_reader :type
1634
1293
 
1294
+ def initialize(attributes = {})
1295
+ super
1635
1296
  @type = 'local'
1636
- @path = path
1637
1297
  end
1638
1298
 
1639
1299
  def to_h
@@ -1642,29 +1302,11 @@ module ClaudeAgentSDK
1642
1302
  end
1643
1303
 
1644
1304
  # Sandbox network configuration
1645
- class SandboxNetworkConfig
1305
+ class SandboxNetworkConfig < Type
1646
1306
  attr_accessor :allowed_domains, :allow_managed_domains_only,
1647
1307
  :allow_unix_sockets, :allow_all_unix_sockets, :allow_local_binding,
1648
1308
  :http_proxy_port, :socks_proxy_port
1649
1309
 
1650
- def initialize(
1651
- allowed_domains: nil,
1652
- allow_managed_domains_only: nil,
1653
- allow_unix_sockets: nil,
1654
- allow_all_unix_sockets: nil,
1655
- allow_local_binding: nil,
1656
- http_proxy_port: nil,
1657
- socks_proxy_port: nil
1658
- )
1659
- @allowed_domains = allowed_domains # Array of domain strings
1660
- @allow_managed_domains_only = allow_managed_domains_only
1661
- @allow_unix_sockets = allow_unix_sockets # macOS only: Array of socket paths
1662
- @allow_all_unix_sockets = allow_all_unix_sockets
1663
- @allow_local_binding = allow_local_binding
1664
- @http_proxy_port = http_proxy_port
1665
- @socks_proxy_port = socks_proxy_port
1666
- end
1667
-
1668
1310
  def to_h
1669
1311
  result = {}
1670
1312
  result[:allowedDomains] = @allowed_domains if @allowed_domains
@@ -1679,18 +1321,9 @@ module ClaudeAgentSDK
1679
1321
  end
1680
1322
 
1681
1323
  # Sandbox filesystem configuration
1682
- class SandboxFilesystemConfig
1324
+ class SandboxFilesystemConfig < Type
1683
1325
  attr_accessor :allow_write, :deny_write, :deny_read, :allow_read, :allow_managed_read_paths_only
1684
1326
 
1685
- def initialize(allow_write: nil, deny_write: nil, deny_read: nil, allow_read: nil,
1686
- allow_managed_read_paths_only: nil)
1687
- @allow_write = allow_write # Array of paths to allow writing
1688
- @deny_write = deny_write # Array of paths to deny writing
1689
- @deny_read = deny_read # Array of paths to deny reading
1690
- @allow_read = allow_read # Array of paths to re-allow reading within denyRead
1691
- @allow_managed_read_paths_only = allow_managed_read_paths_only
1692
- end
1693
-
1694
1327
  def to_h
1695
1328
  result = {}
1696
1329
  result[:allowWrite] = @allow_write if @allow_write
@@ -1703,38 +1336,12 @@ module ClaudeAgentSDK
1703
1336
  end
1704
1337
 
1705
1338
  # Sandbox settings for isolated command execution
1706
- class SandboxSettings
1339
+ class SandboxSettings < Type
1707
1340
  attr_accessor :enabled, :fail_if_unavailable, :auto_allow_bash_if_sandboxed,
1708
1341
  :excluded_commands, :allow_unsandboxed_commands, :network, :filesystem,
1709
1342
  :ignore_violations, :enable_weaker_nested_sandbox,
1710
1343
  :enable_weaker_network_isolation, :ripgrep
1711
1344
 
1712
- def initialize(
1713
- enabled: nil,
1714
- fail_if_unavailable: nil,
1715
- auto_allow_bash_if_sandboxed: nil,
1716
- excluded_commands: nil,
1717
- allow_unsandboxed_commands: nil,
1718
- network: nil,
1719
- filesystem: nil,
1720
- ignore_violations: nil,
1721
- enable_weaker_nested_sandbox: nil,
1722
- enable_weaker_network_isolation: nil,
1723
- ripgrep: nil
1724
- )
1725
- @enabled = enabled
1726
- @fail_if_unavailable = fail_if_unavailable
1727
- @auto_allow_bash_if_sandboxed = auto_allow_bash_if_sandboxed
1728
- @excluded_commands = excluded_commands # Array of commands to exclude
1729
- @allow_unsandboxed_commands = allow_unsandboxed_commands
1730
- @network = network # SandboxNetworkConfig instance
1731
- @filesystem = filesystem # SandboxFilesystemConfig instance
1732
- @ignore_violations = ignore_violations # Hash of { category => [patterns] }
1733
- @enable_weaker_nested_sandbox = enable_weaker_nested_sandbox
1734
- @enable_weaker_network_isolation = enable_weaker_network_isolation # macOS only
1735
- @ripgrep = ripgrep # Hash with :command and optional :args
1736
- end
1737
-
1738
1345
  def to_h
1739
1346
  result = {}
1740
1347
  result[:enabled] = @enabled unless @enabled.nil?
@@ -1753,36 +1360,29 @@ module ClaudeAgentSDK
1753
1360
  end
1754
1361
 
1755
1362
  # Result of a session fork operation
1756
- class ForkSessionResult
1363
+ class ForkSessionResult < Type
1757
1364
  attr_accessor :session_id
1758
-
1759
- def initialize(session_id:)
1760
- @session_id = session_id
1761
- end
1762
1365
  end
1763
1366
 
1764
1367
  # API-side task budget in tokens.
1765
1368
  # When set, the model is made aware of its remaining token budget so it can
1766
1369
  # pace tool use and wrap up before the limit.
1767
- class TaskBudget
1370
+ class TaskBudget < Type
1768
1371
  attr_accessor :total
1769
1372
 
1770
- def initialize(total:)
1771
- @total = total
1772
- end
1773
-
1774
1373
  def to_h
1775
1374
  { total: @total }
1776
1375
  end
1777
1376
  end
1778
1377
 
1779
1378
  # System prompt file configuration — loads system prompt from a file path
1780
- class SystemPromptFile
1781
- attr_accessor :type, :path
1379
+ class SystemPromptFile < Type
1380
+ attr_accessor :path
1381
+ attr_reader :type
1782
1382
 
1783
- def initialize(path:)
1383
+ def initialize(attributes = {})
1384
+ super
1784
1385
  @type = 'file'
1785
- @path = path
1786
1386
  end
1787
1387
 
1788
1388
  def to_h
@@ -1791,14 +1391,13 @@ module ClaudeAgentSDK
1791
1391
  end
1792
1392
 
1793
1393
  # System prompt preset configuration
1794
- class SystemPromptPreset
1795
- attr_accessor :type, :preset, :append, :exclude_dynamic_sections
1394
+ class SystemPromptPreset < Type
1395
+ attr_reader :type
1396
+ attr_accessor :preset, :append, :exclude_dynamic_sections
1796
1397
 
1797
- def initialize(preset:, append: nil, exclude_dynamic_sections: nil)
1398
+ def initialize(attributes = {})
1399
+ super
1798
1400
  @type = 'preset'
1799
- @preset = preset
1800
- @append = append
1801
- @exclude_dynamic_sections = exclude_dynamic_sections
1802
1401
  end
1803
1402
 
1804
1403
  def to_h
@@ -1810,12 +1409,13 @@ module ClaudeAgentSDK
1810
1409
  end
1811
1410
 
1812
1411
  # Tools preset configuration
1813
- class ToolsPreset
1814
- attr_accessor :type, :preset
1412
+ class ToolsPreset < Type
1413
+ attr_reader :type
1414
+ attr_accessor :preset
1815
1415
 
1816
- def initialize(preset:)
1416
+ def initialize(attributes = {})
1417
+ super
1817
1418
  @type = 'preset'
1818
- @preset = preset
1819
1419
  end
1820
1420
 
1821
1421
  def to_h
@@ -1824,64 +1424,108 @@ module ClaudeAgentSDK
1824
1424
  end
1825
1425
 
1826
1426
  # Claude Agent Options for configuring queries
1827
- class ClaudeAgentOptions
1427
+ class ClaudeAgentOptions < Type
1828
1428
  attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
1829
- :continue_conversation, :resume, :session_id, :max_turns, :disallowed_tools,
1429
+ :resume, :resume_session_at, :session_id, :max_turns, :disallowed_tools,
1830
1430
  :model, :permission_prompt_tool_name, :cwd, :cli_path, :settings,
1831
1431
  :add_dirs, :env, :extra_args, :max_buffer_size, :stderr,
1832
- :can_use_tool, :hooks, :user, :include_partial_messages,
1833
- :fork_session, :agents, :setting_sources,
1432
+ :can_use_tool, :hooks, :user,
1433
+ :agents, :setting_sources,
1834
1434
  :output_format, :max_budget_usd, :max_thinking_tokens,
1835
1435
  :fallback_model, :plugins, :debug_stderr,
1836
- :betas, :tools, :sandbox, :enable_file_checkpointing, :append_allowed_tools,
1837
- :thinking, :effort, :bare, :observers, :task_budget
1838
-
1839
- # Non-nil defaults for options that need them.
1840
- # Keys absent from here default to nil.
1841
- OPTION_DEFAULTS = {
1842
- allowed_tools: [], disallowed_tools: [], add_dirs: [],
1843
- mcp_servers: {}, env: {}, extra_args: {},
1844
- continue_conversation: false, include_partial_messages: false,
1845
- fork_session: false, enable_file_checkpointing: false,
1846
- observers: []
1847
- }.freeze
1848
-
1849
- # Valid option names derived from attr_accessor declarations.
1850
- VALID_OPTIONS = instance_methods.grep(/=\z/).map { |m| m.to_s.chomp('=').to_sym }.freeze
1851
-
1852
- # Using **kwargs lets us distinguish "caller passed allowed_tools: []"
1853
- # from "caller omitted allowed_tools" — critical for correct merge with
1854
- # configured defaults.
1855
- def initialize(**kwargs)
1856
- unknown = kwargs.keys - VALID_OPTIONS
1857
- raise ArgumentError, "unknown keyword#{'s' if unknown.size > 1}: #{unknown.join(', ')}" if unknown.any?
1858
-
1859
- merged = merge_with_defaults(kwargs)
1860
- OPTION_DEFAULTS.merge(merged).each do |key, value|
1861
- instance_variable_set(:"@#{key}", value)
1862
- end
1436
+ :betas, :tools, :sandbox, :append_allowed_tools,
1437
+ :thinking, :effort, :observers, :task_budget
1438
+ attr_reader :bare, :fork_session, :enable_file_checkpointing,
1439
+ :include_partial_messages, :continue_conversation
1440
+
1441
+ def initialize(attributes = {})
1442
+ self.fork_session = false
1443
+ self.continue_conversation = false
1444
+ self.include_partial_messages = false
1445
+ self.enable_file_checkpointing = false
1446
+
1447
+ super(merge_with_defaults(attributes || {}))
1448
+
1449
+ # Non-nil defaults for options that need them.
1450
+ self.env ||= {}
1451
+ self.extra_args ||= {}
1452
+ self.mcp_servers ||= {}
1453
+ self.add_dirs ||= []
1454
+ self.observers ||= []
1455
+ self.allowed_tools ||= []
1456
+ self.disallowed_tools ||= []
1863
1457
  end
1864
1458
 
1865
1459
  def dup_with(**changes)
1866
1460
  new_options = self.dup
1867
- changes.each { |key, value| new_options.send(:"#{key}=", value) }
1461
+ changes.each { |key, value| new_options[key] = value }
1868
1462
  new_options
1869
1463
  end
1870
1464
 
1465
+ def bare?
1466
+ !!bare
1467
+ end
1468
+
1469
+ def bare=(value)
1470
+ @bare = coerce_boolean(value)
1471
+ end
1472
+
1473
+ def fork_session?
1474
+ !!fork_session
1475
+ end
1476
+
1477
+ def fork_session=(value)
1478
+ @fork_session = coerce_boolean(value)
1479
+ end
1480
+
1481
+ def enable_file_checkpointing?
1482
+ !!enable_file_checkpointing
1483
+ end
1484
+
1485
+ def enable_file_checkpointing=(value)
1486
+ @enable_file_checkpointing = coerce_boolean(value)
1487
+ end
1488
+
1489
+ def include_partial_messages?
1490
+ !!include_partial_messages
1491
+ end
1492
+
1493
+ def include_partial_messages=(value)
1494
+ @include_partial_messages = coerce_boolean(value)
1495
+ end
1496
+
1497
+ def continue_conversation?
1498
+ !!continue_conversation
1499
+ end
1500
+
1501
+ def continue_conversation=(value)
1502
+ @continue_conversation = coerce_boolean(value)
1503
+ end
1504
+
1871
1505
  private
1872
1506
 
1873
- # Merge caller-provided kwargs with configured defaults.
1507
+ # Strict key validation: unlike other Type subclasses (which silently drop
1508
+ # unknown keys for forward-compat with newer CLI output), ClaudeAgentOptions
1509
+ # is a developer-facing config object — typos should fail loudly.
1510
+ def assign_attribute(name, value)
1511
+ setter = :"#{normalize_name(name)}="
1512
+ raise ArgumentError, "unknown ClaudeAgentOptions option: #{name.inspect}" unless respond_to?(setter)
1513
+
1514
+ public_send(setter, value)
1515
+ end
1516
+
1517
+ # Merge caller-provided attributes with configured defaults.
1874
1518
  # Only keys the caller explicitly passed are treated as overrides;
1875
- # method-signature defaults ([], {}, false) are NOT in kwargs unless the caller wrote them.
1876
- def merge_with_defaults(kwargs)
1877
- return OPTION_DEFAULTS.merge(kwargs) unless defined?(ClaudeAgentSDK) && ClaudeAgentSDK.respond_to?(:default_options)
1519
+ # method-signature defaults ([], {}, false) are NOT present unless the caller wrote them.
1520
+ def merge_with_defaults(attributes)
1521
+ return attributes unless defined?(ClaudeAgentSDK) && ClaudeAgentSDK.respond_to?(:default_options)
1878
1522
 
1879
1523
  defaults = ClaudeAgentSDK.default_options
1880
- return OPTION_DEFAULTS.merge(kwargs) unless defaults.any?
1524
+ return attributes unless defaults.any?
1881
1525
 
1882
1526
  # Start from configured defaults (deep dup hashes to prevent mutation)
1883
1527
  result = defaults.transform_values { |v| v.is_a?(Hash) ? v.dup : v }
1884
- kwargs.each do |key, value|
1528
+ attributes.each do |key, value|
1885
1529
  default_val = result[key]
1886
1530
  result[key] = if value.nil?
1887
1531
  default_val # nil means "no preference" — keep the configured default
@@ -1891,46 +1535,22 @@ module ClaudeAgentSDK
1891
1535
  value
1892
1536
  end
1893
1537
  end
1894
- OPTION_DEFAULTS.merge(result)
1538
+ result
1895
1539
  end
1896
1540
  end
1897
1541
 
1898
1542
  # SDK MCP Tool definition
1899
- class SdkMcpTool
1543
+ class SdkMcpTool < Type
1900
1544
  attr_accessor :name, :description, :input_schema, :handler, :annotations, :meta
1901
-
1902
- def initialize(name:, description:, input_schema:, handler:, annotations: nil, meta: nil)
1903
- @name = name
1904
- @description = description
1905
- @input_schema = input_schema
1906
- @handler = handler
1907
- @annotations = annotations # MCP tool annotations (e.g., { title: '...', readOnlyHint: true })
1908
- @meta = meta # MCP _meta field (e.g., { 'anthropic/maxResultSizeChars' => 100000 })
1909
- end
1910
1545
  end
1911
1546
 
1912
1547
  # SDK MCP Resource definition
1913
- class SdkMcpResource
1548
+ class SdkMcpResource < Type
1914
1549
  attr_accessor :uri, :name, :description, :mime_type, :reader
1915
-
1916
- def initialize(uri:, name:, description: nil, mime_type: nil, reader:)
1917
- @uri = uri
1918
- @name = name
1919
- @description = description
1920
- @mime_type = mime_type
1921
- @reader = reader
1922
- end
1923
1550
  end
1924
1551
 
1925
1552
  # SDK MCP Prompt definition
1926
- class SdkMcpPrompt
1553
+ class SdkMcpPrompt < Type
1927
1554
  attr_accessor :name, :description, :arguments, :generator
1928
-
1929
- def initialize(name:, description: nil, arguments: nil, generator:)
1930
- @name = name
1931
- @description = description
1932
- @arguments = arguments
1933
- @generator = generator
1934
- end
1935
1555
  end
1936
1556
  end