claude_agent 0.7.12 → 0.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/rules/testing.md +51 -10
  3. data/.claude/settings.json +1 -0
  4. data/ARCHITECTURE.md +237 -0
  5. data/CHANGELOG.md +45 -0
  6. data/CLAUDE.md +2 -0
  7. data/README.md +46 -1
  8. data/Rakefile +17 -0
  9. data/SPEC.md +214 -125
  10. data/lib/claude_agent/client/commands.rb +225 -0
  11. data/lib/claude_agent/client.rb +4 -204
  12. data/lib/claude_agent/content_blocks/generic_block.rb +39 -0
  13. data/lib/claude_agent/content_blocks/image_content_block.rb +54 -0
  14. data/lib/claude_agent/content_blocks/server_tool_result_block.rb +22 -0
  15. data/lib/claude_agent/content_blocks/server_tool_use_block.rb +48 -0
  16. data/lib/claude_agent/content_blocks/text_block.rb +19 -0
  17. data/lib/claude_agent/content_blocks/thinking_block.rb +19 -0
  18. data/lib/claude_agent/content_blocks/tool_result_block.rb +25 -0
  19. data/lib/claude_agent/content_blocks/tool_use_block.rb +134 -0
  20. data/lib/claude_agent/content_blocks.rb +8 -335
  21. data/lib/claude_agent/control_protocol/commands.rb +304 -0
  22. data/lib/claude_agent/control_protocol/lifecycle.rb +113 -0
  23. data/lib/claude_agent/control_protocol/messaging.rb +166 -0
  24. data/lib/claude_agent/control_protocol/primitives.rb +168 -0
  25. data/lib/claude_agent/control_protocol/request_handling.rb +231 -0
  26. data/lib/claude_agent/control_protocol.rb +27 -882
  27. data/lib/claude_agent/event_handler.rb +1 -0
  28. data/lib/claude_agent/get_session_info.rb +86 -0
  29. data/lib/claude_agent/hooks.rb +23 -2
  30. data/lib/claude_agent/list_sessions.rb +22 -13
  31. data/lib/claude_agent/message_parser.rb +26 -4
  32. data/lib/claude_agent/messages/conversation.rb +138 -0
  33. data/lib/claude_agent/messages/generic.rb +39 -0
  34. data/lib/claude_agent/messages/hook_lifecycle.rb +158 -0
  35. data/lib/claude_agent/messages/result.rb +80 -0
  36. data/lib/claude_agent/messages/streaming.rb +84 -0
  37. data/lib/claude_agent/messages/system.rb +67 -0
  38. data/lib/claude_agent/messages/task_lifecycle.rb +240 -0
  39. data/lib/claude_agent/messages/tool_lifecycle.rb +95 -0
  40. data/lib/claude_agent/messages.rb +11 -829
  41. data/lib/claude_agent/options/serializer.rb +194 -0
  42. data/lib/claude_agent/options.rb +11 -176
  43. data/lib/claude_agent/sandbox_settings.rb +3 -0
  44. data/lib/claude_agent/session.rb +0 -204
  45. data/lib/claude_agent/session_mutations.rb +148 -0
  46. data/lib/claude_agent/types/mcp.rb +30 -0
  47. data/lib/claude_agent/types/models.rb +146 -0
  48. data/lib/claude_agent/types/operations.rb +38 -0
  49. data/lib/claude_agent/types/sessions.rb +50 -0
  50. data/lib/claude_agent/types/tools.rb +32 -0
  51. data/lib/claude_agent/types.rb +6 -264
  52. data/lib/claude_agent/v2_session.rb +207 -0
  53. data/lib/claude_agent/version.rb +1 -1
  54. data/lib/claude_agent.rb +37 -3
  55. data/sig/claude_agent.rbs +144 -13
  56. metadata +33 -1
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ class Client
5
+ # CLI command delegations to the ControlProtocol.
6
+ #
7
+ # These methods provide the Client-level interface to control commands:
8
+ # permission/model changes, MCP management, task control, and query methods.
9
+ # Each enforces connection state before delegating to the protocol.
10
+ module Commands
11
+ # Change the permission mode
12
+ #
13
+ # @param mode [String] New permission mode
14
+ # @return [Hash] Response
15
+ def set_permission_mode(mode)
16
+ require_connection!
17
+
18
+ @protocol.set_permission_mode(mode)
19
+ end
20
+
21
+ # Change the model
22
+ #
23
+ # @param model [String, nil] New model name (nil to use default)
24
+ # @return [Hash] Response
25
+ def set_model(model)
26
+ require_connection!
27
+
28
+ @protocol.set_model(model)
29
+ end
30
+
31
+ # Rewind files to the state at a specific user message
32
+ #
33
+ # @param user_message_id [String] UUID of the user message to rewind to
34
+ # @param dry_run [Boolean] If true, preview changes without modifying files
35
+ # @return [RewindFilesResult] Result with rewind information
36
+ def rewind_files(user_message_id, dry_run: false)
37
+ require_connection!
38
+
39
+ @protocol.rewind_files(user_message_id, dry_run: dry_run)
40
+ end
41
+
42
+ # Set maximum thinking tokens (TypeScript SDK parity)
43
+ #
44
+ # @param tokens [Integer, nil] Max thinking tokens (nil to reset)
45
+ # @return [Hash] Response
46
+ def set_max_thinking_tokens(tokens)
47
+ require_connection!
48
+
49
+ @protocol.set_max_thinking_tokens(tokens)
50
+ end
51
+
52
+ # Get available slash commands (TypeScript SDK parity)
53
+ #
54
+ # @return [Array<SlashCommand>]
55
+ def supported_commands
56
+ require_connection!
57
+
58
+ @protocol.supported_commands
59
+ end
60
+
61
+ # Get available models (TypeScript SDK parity)
62
+ #
63
+ # @return [Array<ModelInfo>]
64
+ def supported_models
65
+ require_connection!
66
+
67
+ @protocol.supported_models
68
+ end
69
+
70
+ # Get available agents (TypeScript SDK v0.2.63 parity)
71
+ #
72
+ # @return [Array<AgentInfo>]
73
+ def supported_agents
74
+ require_connection!
75
+
76
+ @protocol.supported_agents
77
+ end
78
+
79
+ # Get MCP server status (TypeScript SDK parity)
80
+ #
81
+ # @return [Array<McpServerStatus>]
82
+ def mcp_server_status
83
+ require_connection!
84
+
85
+ @protocol.mcp_server_status
86
+ end
87
+
88
+ # Get account information (TypeScript SDK parity)
89
+ #
90
+ # @return [AccountInfo]
91
+ def account_info
92
+ require_connection!
93
+
94
+ @protocol.account_info
95
+ end
96
+
97
+ # Get full initialization result (TypeScript SDK parity)
98
+ #
99
+ # @return [InitializationResult]
100
+ def initialization_result
101
+ require_connection!
102
+
103
+ @protocol.initialization_result
104
+ end
105
+
106
+ # Stop a running background task (TypeScript SDK parity)
107
+ #
108
+ # Sends a stop signal to a running task. A task_notification message
109
+ # with status 'stopped' will be emitted when the task stops.
110
+ #
111
+ # @param task_id [String] The task ID from task_notification events
112
+ # @return [void]
113
+ #
114
+ # @example
115
+ # client.stop_task("task-123")
116
+ #
117
+ def stop_task(task_id)
118
+ require_connection!
119
+
120
+ @protocol.stop_task(task_id)
121
+ end
122
+
123
+ # Apply flag settings (TypeScript SDK v0.2.50 parity)
124
+ #
125
+ # Merges the provided settings into the flag settings layer.
126
+ #
127
+ # @param settings [Hash] Settings to merge into the flag layer
128
+ # @return [Hash] Response from the CLI
129
+ #
130
+ # @example
131
+ # client.apply_flag_settings({ "model" => "claude-sonnet-4-5-20250514" })
132
+ #
133
+ def apply_flag_settings(settings)
134
+ require_connection!
135
+
136
+ @protocol.apply_flag_settings(settings)
137
+ end
138
+
139
+ # Dynamically set MCP servers for this session (TypeScript SDK parity)
140
+ #
141
+ # This replaces the current set of dynamically-added MCP servers.
142
+ # Servers that are removed will be disconnected, and new servers will be connected.
143
+ #
144
+ # @param servers [Hash] Map of server name to configuration
145
+ # @return [McpSetServersResult] Result with added, removed, and errors
146
+ #
147
+ # @example
148
+ # result = client.set_mcp_servers({
149
+ # "my-server" => { type: "stdio", command: "node", args: ["server.js"] }
150
+ # })
151
+ # puts "Added: #{result.added}"
152
+ # puts "Removed: #{result.removed}"
153
+ #
154
+ def set_mcp_servers(servers)
155
+ require_connection!
156
+
157
+ @protocol.set_mcp_servers(servers)
158
+ end
159
+
160
+ # Reconnect to an MCP server (TypeScript SDK parity)
161
+ #
162
+ # Attempts to reconnect to a disconnected or errored MCP server.
163
+ #
164
+ # @param server_name [String] Name of the MCP server to reconnect
165
+ # @return [Hash] Response from the CLI
166
+ #
167
+ # @example
168
+ # client.mcp_reconnect("my-server")
169
+ #
170
+ def mcp_reconnect(server_name)
171
+ require_connection!
172
+
173
+ @protocol.mcp_reconnect(server_name)
174
+ end
175
+
176
+ # Enable or disable an MCP server (TypeScript SDK parity)
177
+ #
178
+ # Toggles an MCP server on or off without removing its configuration.
179
+ #
180
+ # @param server_name [String] Name of the MCP server to toggle
181
+ # @param enabled [Boolean] Whether to enable (true) or disable (false) the server
182
+ # @return [Hash] Response from the CLI
183
+ #
184
+ # @example Enable a server
185
+ # client.mcp_toggle("my-server", enabled: true)
186
+ #
187
+ # @example Disable a server
188
+ # client.mcp_toggle("my-server", enabled: false)
189
+ #
190
+ def mcp_toggle(server_name, enabled:)
191
+ require_connection!
192
+
193
+ @protocol.mcp_toggle(server_name, enabled: enabled)
194
+ end
195
+
196
+ # Initiate OAuth authentication for an MCP server (TypeScript SDK v0.2.52 parity)
197
+ #
198
+ # @param server_name [String] Name of the MCP server to authenticate
199
+ # @return [Hash] Response from the CLI
200
+ #
201
+ # @example
202
+ # client.mcp_authenticate("my-remote-server")
203
+ #
204
+ def mcp_authenticate(server_name)
205
+ require_connection!
206
+
207
+ @protocol.mcp_authenticate(server_name)
208
+ end
209
+
210
+ # Clear stored auth credentials for an MCP server (TypeScript SDK v0.2.52 parity)
211
+ #
212
+ # @param server_name [String] Name of the MCP server to clear auth for
213
+ # @return [Hash] Response from the CLI
214
+ #
215
+ # @example
216
+ # client.mcp_clear_auth("my-remote-server")
217
+ #
218
+ def mcp_clear_auth(server_name)
219
+ require_connection!
220
+
221
+ @protocol.mcp_clear_auth(server_name)
222
+ end
223
+ end
224
+ end
225
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "client/commands"
4
+
3
5
  module ClaudeAgent
4
6
  # Interactive, bidirectional client for Claude Code CLI
5
7
  #
@@ -32,6 +34,8 @@ module ClaudeAgent
32
34
  # end
33
35
  #
34
36
  class Client
37
+ include Commands
38
+
35
39
  attr_reader :options, :transport, :server_info, :cumulative_usage, :event_handler, :permission_queue
36
40
 
37
41
  # Open a client with automatic cleanup
@@ -295,210 +299,6 @@ module ClaudeAgent
295
299
  @protocol&.abort!
296
300
  end
297
301
 
298
- # Change the permission mode
299
- #
300
- # @param mode [String] New permission mode
301
- # @return [Hash] Response
302
- def set_permission_mode(mode)
303
- require_connection!
304
-
305
- @protocol.set_permission_mode(mode)
306
- end
307
-
308
- # Change the model
309
- #
310
- # @param model [String, nil] New model name (nil to use default)
311
- # @return [Hash] Response
312
- def set_model(model)
313
- require_connection!
314
-
315
- @protocol.set_model(model)
316
- end
317
-
318
- # Rewind files to the state at a specific user message
319
- #
320
- # @param user_message_id [String] UUID of the user message to rewind to
321
- # @param dry_run [Boolean] If true, preview changes without modifying files
322
- # @return [RewindFilesResult] Result with rewind information
323
- def rewind_files(user_message_id, dry_run: false)
324
- require_connection!
325
-
326
- @protocol.rewind_files(user_message_id, dry_run: dry_run)
327
- end
328
-
329
- # Set maximum thinking tokens (TypeScript SDK parity)
330
- #
331
- # @param tokens [Integer, nil] Max thinking tokens (nil to reset)
332
- # @return [Hash] Response
333
- def set_max_thinking_tokens(tokens)
334
- require_connection!
335
-
336
- @protocol.set_max_thinking_tokens(tokens)
337
- end
338
-
339
- # Get available slash commands (TypeScript SDK parity)
340
- #
341
- # @return [Array<SlashCommand>]
342
- def supported_commands
343
- require_connection!
344
-
345
- @protocol.supported_commands
346
- end
347
-
348
- # Get available models (TypeScript SDK parity)
349
- #
350
- # @return [Array<ModelInfo>]
351
- def supported_models
352
- require_connection!
353
-
354
- @protocol.supported_models
355
- end
356
-
357
- # Get MCP server status (TypeScript SDK parity)
358
- #
359
- # @return [Array<McpServerStatus>]
360
- def mcp_server_status
361
- require_connection!
362
-
363
- @protocol.mcp_server_status
364
- end
365
-
366
- # Get account information (TypeScript SDK parity)
367
- #
368
- # @return [AccountInfo]
369
- def account_info
370
- require_connection!
371
-
372
- @protocol.account_info
373
- end
374
-
375
- # Get full initialization result (TypeScript SDK parity)
376
- #
377
- # @return [InitializationResult]
378
- def initialization_result
379
- require_connection!
380
-
381
- @protocol.initialization_result
382
- end
383
-
384
- # Stop a running background task (TypeScript SDK parity)
385
- #
386
- # Sends a stop signal to a running task. A task_notification message
387
- # with status 'stopped' will be emitted when the task stops.
388
- #
389
- # @param task_id [String] The task ID from task_notification events
390
- # @return [void]
391
- #
392
- # @example
393
- # client.stop_task("task-123")
394
- #
395
- def stop_task(task_id)
396
- require_connection!
397
-
398
- @protocol.stop_task(task_id)
399
- end
400
-
401
- # Apply flag settings (TypeScript SDK v0.2.50 parity)
402
- #
403
- # Merges the provided settings into the flag settings layer.
404
- #
405
- # @param settings [Hash] Settings to merge into the flag layer
406
- # @return [Hash] Response from the CLI
407
- #
408
- # @example
409
- # client.apply_flag_settings({ "model" => "claude-sonnet-4-5-20250514" })
410
- #
411
- def apply_flag_settings(settings)
412
- require_connection!
413
-
414
- @protocol.apply_flag_settings(settings)
415
- end
416
-
417
- # Dynamically set MCP servers for this session (TypeScript SDK parity)
418
- #
419
- # This replaces the current set of dynamically-added MCP servers.
420
- # Servers that are removed will be disconnected, and new servers will be connected.
421
- #
422
- # @param servers [Hash] Map of server name to configuration
423
- # @return [McpSetServersResult] Result with added, removed, and errors
424
- #
425
- # @example
426
- # result = client.set_mcp_servers({
427
- # "my-server" => { type: "stdio", command: "node", args: ["server.js"] }
428
- # })
429
- # puts "Added: #{result.added}"
430
- # puts "Removed: #{result.removed}"
431
- #
432
- def set_mcp_servers(servers)
433
- require_connection!
434
-
435
- @protocol.set_mcp_servers(servers)
436
- end
437
-
438
- # Reconnect to an MCP server (TypeScript SDK parity)
439
- #
440
- # Attempts to reconnect to a disconnected or errored MCP server.
441
- #
442
- # @param server_name [String] Name of the MCP server to reconnect
443
- # @return [Hash] Response from the CLI
444
- #
445
- # @example
446
- # client.mcp_reconnect("my-server")
447
- #
448
- def mcp_reconnect(server_name)
449
- require_connection!
450
-
451
- @protocol.mcp_reconnect(server_name)
452
- end
453
-
454
- # Enable or disable an MCP server (TypeScript SDK parity)
455
- #
456
- # Toggles an MCP server on or off without removing its configuration.
457
- #
458
- # @param server_name [String] Name of the MCP server to toggle
459
- # @param enabled [Boolean] Whether to enable (true) or disable (false) the server
460
- # @return [Hash] Response from the CLI
461
- #
462
- # @example Enable a server
463
- # client.mcp_toggle("my-server", enabled: true)
464
- #
465
- # @example Disable a server
466
- # client.mcp_toggle("my-server", enabled: false)
467
- #
468
- def mcp_toggle(server_name, enabled:)
469
- require_connection!
470
-
471
- @protocol.mcp_toggle(server_name, enabled: enabled)
472
- end
473
-
474
- # Initiate OAuth authentication for an MCP server (TypeScript SDK v0.2.52 parity)
475
- #
476
- # @param server_name [String] Name of the MCP server to authenticate
477
- # @return [Hash] Response from the CLI
478
- #
479
- # @example
480
- # client.mcp_authenticate("my-remote-server")
481
- #
482
- def mcp_authenticate(server_name)
483
- require_connection!
484
-
485
- @protocol.mcp_authenticate(server_name)
486
- end
487
-
488
- # Clear stored auth credentials for an MCP server (TypeScript SDK v0.2.52 parity)
489
- #
490
- # @param server_name [String] Name of the MCP server to clear auth for
491
- # @return [Hash] Response from the CLI
492
- #
493
- # @example
494
- # client.mcp_clear_auth("my-remote-server")
495
- #
496
- def mcp_clear_auth(server_name)
497
- require_connection!
498
-
499
- @protocol.mcp_clear_auth(server_name)
500
- end
501
-
502
302
  # Non-blocking poll for the next pending permission request.
503
303
  #
504
304
  # Returns the next {PermissionRequest} from the queue, or nil if
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Generic content block for unknown/future block types
5
+ #
6
+ # Wraps unrecognized content block types so they can be inspected
7
+ # without losing type information. Supports dynamic field access via
8
+ # `[]` and `method_missing`.
9
+ #
10
+ # @example
11
+ # block = GenericBlock.new(block_type: "citation", raw: { text: "ref", url: "https://example.com" })
12
+ # block.type # => :citation
13
+ # block[:text] # => "ref"
14
+ # block.url # => "https://example.com"
15
+ # block.to_h # => { text: "ref", url: "https://example.com" }
16
+ #
17
+ GenericBlock = Data.define(:block_type, :raw) do
18
+ def type
19
+ block_type&.to_sym || :unknown
20
+ end
21
+
22
+ def to_h
23
+ raw
24
+ end
25
+
26
+ def [](key)
27
+ raw[key]
28
+ end
29
+
30
+ def respond_to_missing?(name, include_private = false)
31
+ raw.key?(name) || super
32
+ end
33
+
34
+ def method_missing(name, *args)
35
+ return raw[name] if args.empty? && raw.key?(name)
36
+ super
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Image content block (TypeScript SDK parity)
5
+ #
6
+ # Supports both base64-encoded image data and URL sources.
7
+ #
8
+ # @example Base64 image
9
+ # block = ImageContentBlock.new(
10
+ # source: { type: "base64", media_type: "image/png", data: "..." }
11
+ # )
12
+ # block.source_type # => "base64"
13
+ # block.media_type # => "image/png"
14
+ #
15
+ # @example URL image
16
+ # block = ImageContentBlock.new(
17
+ # source: { type: "url", url: "https://example.com/image.png" }
18
+ # )
19
+ # block.url # => "https://example.com/image.png"
20
+ #
21
+ ImageContentBlock = Data.define(:source) do
22
+ def type
23
+ :image
24
+ end
25
+
26
+ # Get the media type if available
27
+ # @return [String, nil]
28
+ def media_type
29
+ source.is_a?(Hash) ? source[:media_type] : nil
30
+ end
31
+
32
+ # Get the base64 data if available
33
+ # @return [String, nil]
34
+ def data
35
+ source.is_a?(Hash) ? source[:data] : nil
36
+ end
37
+
38
+ # Get the URL if this is a URL-sourced image
39
+ # @return [String, nil]
40
+ def url
41
+ source.is_a?(Hash) ? source[:url] : nil
42
+ end
43
+
44
+ # Get the source type (base64 or url)
45
+ # @return [String, nil]
46
+ def source_type
47
+ source.is_a?(Hash) ? source[:type] : nil
48
+ end
49
+
50
+ def to_h
51
+ { type: "image", source: source }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Server tool result block
5
+ #
6
+ ServerToolResultBlock = Data.define(:tool_use_id, :content, :is_error, :server_name) do
7
+ def initialize(tool_use_id:, server_name:, content: nil, is_error: nil)
8
+ super
9
+ end
10
+
11
+ def type
12
+ :server_tool_result
13
+ end
14
+
15
+ def to_h
16
+ h = { type: "server_tool_result", tool_use_id: tool_use_id, server_name: server_name }
17
+ h[:content] = content unless content.nil?
18
+ h[:is_error] = is_error unless is_error.nil?
19
+ h
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Server tool use block (for MCP servers)
5
+ #
6
+ ServerToolUseBlock = Data.define(:id, :name, :input, :server_name) do
7
+ def type
8
+ :server_tool_use
9
+ end
10
+
11
+ def to_h
12
+ { type: "server_tool_use", id: id, name: name, input: input, server_name: server_name }
13
+ end
14
+
15
+ # Returns the file path for file-based tools, nil otherwise.
16
+ # @return [String, nil]
17
+ def file_path
18
+ case name
19
+ when "Read", "Write", "Edit"
20
+ input[:file_path]
21
+ when "NotebookEdit"
22
+ input[:notebook_path]
23
+ end
24
+ end
25
+
26
+ # One-line human-readable label with server context.
27
+ # @return [String]
28
+ def display_label
29
+ server_name ? "#{server_name}/#{name}" : name
30
+ end
31
+
32
+ # Detailed summary with server context, truncated to max chars.
33
+ # @param max [Integer] maximum length before truncation
34
+ # @return [String]
35
+ def summary(max: 60)
36
+ label = display_label
37
+ text = "#{label}: #{input.inspect}"
38
+ truncate(text, max)
39
+ end
40
+
41
+ private
42
+
43
+ def truncate(str, max)
44
+ return str if str.length <= max
45
+ "#{str[0, max]}..."
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Text content block
5
+ #
6
+ # @example
7
+ # block = TextBlock.new(text: "Hello, world!")
8
+ # block.text # => "Hello, world!"
9
+ #
10
+ TextBlock = Data.define(:text) do
11
+ def type
12
+ :text
13
+ end
14
+
15
+ def to_h
16
+ { type: "text", text: text }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Extended thinking content block
5
+ #
6
+ # @example
7
+ # block = ThinkingBlock.new(thinking: "Let me consider...", signature: "abc123")
8
+ # block.thinking # => "Let me consider..."
9
+ #
10
+ ThinkingBlock = Data.define(:thinking, :signature) do
11
+ def type
12
+ :thinking
13
+ end
14
+
15
+ def to_h
16
+ { type: "thinking", thinking: thinking, signature: signature }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ClaudeAgent
4
+ # Tool result block
5
+ #
6
+ # @example
7
+ # block = ToolResultBlock.new(tool_use_id: "tool_123", content: "file contents", is_error: false)
8
+ #
9
+ ToolResultBlock = Data.define(:tool_use_id, :content, :is_error) do
10
+ def initialize(tool_use_id:, content: nil, is_error: nil)
11
+ super
12
+ end
13
+
14
+ def type
15
+ :tool_result
16
+ end
17
+
18
+ def to_h
19
+ h = { type: "tool_result", tool_use_id: tool_use_id }
20
+ h[:content] = content unless content.nil?
21
+ h[:is_error] = is_error unless is_error.nil?
22
+ h
23
+ end
24
+ end
25
+ end