claude-agent-sdk 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +132 -97
- data/lib/claude_agent_sdk/message_parser.rb +4 -2
- data/lib/claude_agent_sdk/query.rb +80 -4
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +40 -1
- data/lib/claude_agent_sdk/types.rb +263 -8
- data/lib/claude_agent_sdk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1d1549408835720ec2fefea719ca9ccb948b0010bae0bbeb2955ac5e461fd8c
|
|
4
|
+
data.tar.gz: 2b65f85b5ce0dec97e03395652ffeeadf6af4465a5346fc45d587ac20ac93642
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1de77e6f190cc6d474232801b53db8abdf8c239832266b304f6f48399317ab31f8b9298f8ed8af554d142d878476ba81ad712832a117fd01f8abffb798d50be1
|
|
7
|
+
data.tar.gz: 9f9c6ad3bef6990c9e9efaea2f0b49c120b703508a74aaf152836032521b92143d07129f4930569a3bb5de97db1abac7f5d6e1868d9bcfb9bcf9a9fead5e1064
|
data/README.md
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
# Claude Agent SDK for Ruby
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **Disclaimer**: This is an **unofficial, community-maintained** Ruby SDK for Claude Agent. It is not officially supported by Anthropic. For official SDK support, see the [Python SDK](https://docs.claude.com/en/api/agent-sdk/python).
|
|
4
4
|
>
|
|
5
5
|
> This implementation is based on the official Python SDK and aims to provide feature parity for Ruby developers. Use at your own risk.
|
|
6
6
|
|
|
7
|
-
Ruby SDK for Claude Agent. See the [Claude Agent SDK documentation](https://docs.anthropic.com/en/docs/claude-code/sdk) for more information.
|
|
8
|
-
|
|
9
7
|
[](https://badge.fury.io/rb/claude-agent-sdk)
|
|
10
8
|
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Basic Usage: query()](#basic-usage-query)
|
|
14
|
+
- [Client](#client)
|
|
15
|
+
- [Custom Tools (SDK MCP Servers)](#custom-tools-sdk-mcp-servers)
|
|
16
|
+
- [Hooks](#hooks)
|
|
17
|
+
- [Permission Callbacks](#permission-callbacks)
|
|
18
|
+
- [Types](#types)
|
|
19
|
+
- [Error Handling](#error-handling)
|
|
20
|
+
- [Examples](#examples)
|
|
21
|
+
- [Development](#development)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
|
|
11
24
|
## Installation
|
|
12
25
|
|
|
13
26
|
Add this line to your application's Gemfile:
|
|
14
27
|
|
|
15
28
|
```ruby
|
|
16
|
-
gem 'claude-agent-sdk', '~> 0.2.
|
|
29
|
+
gem 'claude-agent-sdk', '~> 0.2.1'
|
|
17
30
|
```
|
|
18
31
|
|
|
19
32
|
And then execute:
|
|
@@ -45,7 +58,7 @@ end
|
|
|
45
58
|
|
|
46
59
|
## Basic Usage: query()
|
|
47
60
|
|
|
48
|
-
`query()` is a function for querying Claude Code. It yields response messages to a block.
|
|
61
|
+
`query()` is a function for querying Claude Code. It yields response messages to a block.
|
|
49
62
|
|
|
50
63
|
```ruby
|
|
51
64
|
require 'claude_agent_sdk'
|
|
@@ -133,11 +146,66 @@ For a complete example, see [examples/streaming_input_example.rb](examples/strea
|
|
|
133
146
|
|
|
134
147
|
`ClaudeAgentSDK::Client` supports bidirectional, interactive conversations with Claude Code. Unlike `query()`, `Client` enables **custom tools**, **hooks**, and **permission callbacks**, all of which can be defined as Ruby procs/lambdas.
|
|
135
148
|
|
|
136
|
-
**The Client class automatically uses streaming mode** for bidirectional communication, allowing you to send multiple queries dynamically during a single session without closing the connection.
|
|
149
|
+
**The Client class automatically uses streaming mode** for bidirectional communication, allowing you to send multiple queries dynamically during a single session without closing the connection.
|
|
150
|
+
|
|
151
|
+
### Basic Client Usage
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
require 'claude_agent_sdk'
|
|
155
|
+
require 'async'
|
|
156
|
+
|
|
157
|
+
Async do
|
|
158
|
+
client = ClaudeAgentSDK::Client.new
|
|
159
|
+
|
|
160
|
+
begin
|
|
161
|
+
# Connect automatically uses streaming mode for bidirectional communication
|
|
162
|
+
client.connect
|
|
163
|
+
|
|
164
|
+
# Send a query
|
|
165
|
+
client.query("What is the capital of France?")
|
|
166
|
+
|
|
167
|
+
# Receive the response
|
|
168
|
+
client.receive_response do |msg|
|
|
169
|
+
if msg.is_a?(ClaudeAgentSDK::AssistantMessage)
|
|
170
|
+
msg.content.each do |block|
|
|
171
|
+
puts block.text if block.is_a?(ClaudeAgentSDK::TextBlock)
|
|
172
|
+
end
|
|
173
|
+
elsif msg.is_a?(ClaudeAgentSDK::ResultMessage)
|
|
174
|
+
puts "Cost: $#{msg.total_cost_usd}" if msg.total_cost_usd
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
ensure
|
|
179
|
+
client.disconnect
|
|
180
|
+
end
|
|
181
|
+
end.wait
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Advanced Client Features
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
Async do
|
|
188
|
+
client = ClaudeAgentSDK::Client.new
|
|
189
|
+
client.connect
|
|
137
190
|
|
|
138
|
-
|
|
191
|
+
# Send interrupt signal
|
|
192
|
+
client.interrupt
|
|
139
193
|
|
|
140
|
-
|
|
194
|
+
# Change permission mode during conversation
|
|
195
|
+
client.set_permission_mode('acceptEdits')
|
|
196
|
+
|
|
197
|
+
# Change AI model during conversation
|
|
198
|
+
client.set_model('claude-sonnet-4-5')
|
|
199
|
+
|
|
200
|
+
# Get server initialization info
|
|
201
|
+
info = client.server_info
|
|
202
|
+
puts "Available commands: #{info}"
|
|
203
|
+
|
|
204
|
+
client.disconnect
|
|
205
|
+
end.wait
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Custom Tools (SDK MCP Servers)
|
|
141
209
|
|
|
142
210
|
A **custom tool** is a Ruby proc/lambda that you can offer to Claude, for Claude to invoke as needed.
|
|
143
211
|
|
|
@@ -145,9 +213,7 @@ Custom tools are implemented as in-process MCP servers that run directly within
|
|
|
145
213
|
|
|
146
214
|
**Implementation**: This SDK uses the [official Ruby MCP SDK](https://github.com/modelcontextprotocol/ruby-sdk) (`mcp` gem) internally, providing full protocol compliance while offering a simpler block-based API for tool definition.
|
|
147
215
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
#### Creating a Simple Tool
|
|
216
|
+
### Creating a Simple Tool
|
|
151
217
|
|
|
152
218
|
```ruby
|
|
153
219
|
require 'claude_agent_sdk'
|
|
@@ -182,7 +248,7 @@ Async do
|
|
|
182
248
|
end.wait
|
|
183
249
|
```
|
|
184
250
|
|
|
185
|
-
|
|
251
|
+
### Benefits Over External MCP Servers
|
|
186
252
|
|
|
187
253
|
- **No subprocess management** - Runs in the same process as your application
|
|
188
254
|
- **Better performance** - No IPC overhead for tool calls
|
|
@@ -190,7 +256,7 @@ end.wait
|
|
|
190
256
|
- **Easier debugging** - All code runs in the same process
|
|
191
257
|
- **Direct access** - Tools can directly access your application's state
|
|
192
258
|
|
|
193
|
-
|
|
259
|
+
### Calculator Example
|
|
194
260
|
|
|
195
261
|
```ruby
|
|
196
262
|
# Define calculator tools
|
|
@@ -220,7 +286,7 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
|
220
286
|
)
|
|
221
287
|
```
|
|
222
288
|
|
|
223
|
-
|
|
289
|
+
### Mixed Server Support
|
|
224
290
|
|
|
225
291
|
You can use both SDK and external MCP servers together:
|
|
226
292
|
|
|
@@ -236,7 +302,7 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
|
236
302
|
)
|
|
237
303
|
```
|
|
238
304
|
|
|
239
|
-
|
|
305
|
+
### MCP Resources and Prompts
|
|
240
306
|
|
|
241
307
|
SDK MCP servers can also expose **resources** (data sources) and **prompts** (reusable templates):
|
|
242
308
|
|
|
@@ -286,48 +352,13 @@ server = ClaudeAgentSDK.create_sdk_mcp_server(
|
|
|
286
352
|
)
|
|
287
353
|
```
|
|
288
354
|
|
|
289
|
-
For complete examples, see [examples/mcp_resources_prompts_example.rb](examples/mcp_resources_prompts_example.rb).
|
|
355
|
+
For complete examples, see [examples/mcp_calculator.rb](examples/mcp_calculator.rb) and [examples/mcp_resources_prompts_example.rb](examples/mcp_resources_prompts_example.rb).
|
|
290
356
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
```ruby
|
|
294
|
-
require 'claude_agent_sdk'
|
|
295
|
-
require 'async'
|
|
296
|
-
|
|
297
|
-
Async do
|
|
298
|
-
client = ClaudeAgentSDK::Client.new
|
|
299
|
-
|
|
300
|
-
begin
|
|
301
|
-
# Connect automatically uses streaming mode for bidirectional communication
|
|
302
|
-
client.connect
|
|
303
|
-
|
|
304
|
-
# Send a query
|
|
305
|
-
client.query("What is the capital of France?")
|
|
306
|
-
|
|
307
|
-
# Receive the response
|
|
308
|
-
client.receive_response do |msg|
|
|
309
|
-
if msg.is_a?(ClaudeAgentSDK::AssistantMessage)
|
|
310
|
-
msg.content.each do |block|
|
|
311
|
-
puts block.text if block.is_a?(ClaudeAgentSDK::TextBlock)
|
|
312
|
-
end
|
|
313
|
-
elsif msg.is_a?(ClaudeAgentSDK::ResultMessage)
|
|
314
|
-
puts "Cost: $#{msg.total_cost_usd}" if msg.total_cost_usd
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
ensure
|
|
319
|
-
client.disconnect
|
|
320
|
-
end
|
|
321
|
-
end.wait
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### Hooks
|
|
357
|
+
## Hooks
|
|
325
358
|
|
|
326
359
|
A **hook** is a Ruby proc/lambda that the Claude Code *application* (*not* Claude) invokes at specific points of the Claude agent loop. Hooks can provide deterministic processing and automated feedback for Claude. Read more in [Claude Code Hooks Reference](https://docs.anthropic.com/en/docs/claude-code/hooks).
|
|
327
360
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
#### Example
|
|
361
|
+
### Example
|
|
331
362
|
|
|
332
363
|
```ruby
|
|
333
364
|
require 'claude_agent_sdk'
|
|
@@ -383,13 +414,13 @@ Async do
|
|
|
383
414
|
end.wait
|
|
384
415
|
```
|
|
385
416
|
|
|
386
|
-
|
|
417
|
+
For more examples, see [examples/hooks_example.rb](examples/hooks_example.rb).
|
|
387
418
|
|
|
388
|
-
|
|
419
|
+
## Permission Callbacks
|
|
389
420
|
|
|
390
|
-
|
|
421
|
+
A **permission callback** is a Ruby proc/lambda that allows you to programmatically control tool execution. This gives you fine-grained control over what tools Claude can use and with what inputs.
|
|
391
422
|
|
|
392
|
-
|
|
423
|
+
### Example
|
|
393
424
|
|
|
394
425
|
```ruby
|
|
395
426
|
require 'claude_agent_sdk'
|
|
@@ -440,38 +471,39 @@ Async do
|
|
|
440
471
|
end.wait
|
|
441
472
|
```
|
|
442
473
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
The Client class supports several advanced features:
|
|
474
|
+
For more examples, see [examples/permission_callback_example.rb](examples/permission_callback_example.rb).
|
|
446
475
|
|
|
447
|
-
|
|
448
|
-
Async do
|
|
449
|
-
client = ClaudeAgentSDK::Client.new
|
|
450
|
-
client.connect
|
|
476
|
+
## Types
|
|
451
477
|
|
|
452
|
-
|
|
453
|
-
client.interrupt
|
|
478
|
+
See [lib/claude_agent_sdk/types.rb](lib/claude_agent_sdk/types.rb) for complete type definitions:
|
|
454
479
|
|
|
455
|
-
|
|
456
|
-
client.set_permission_mode('acceptEdits')
|
|
480
|
+
### Message Types
|
|
457
481
|
|
|
458
|
-
|
|
459
|
-
|
|
482
|
+
| Type | Description |
|
|
483
|
+
|------|-------------|
|
|
484
|
+
| `AssistantMessage` | Response from Claude with content blocks |
|
|
485
|
+
| `UserMessage` | User input message |
|
|
486
|
+
| `SystemMessage` | System metadata message |
|
|
487
|
+
| `ResultMessage` | Final result with cost and usage information |
|
|
488
|
+
| `StreamEvent` | Partial message updates during streaming |
|
|
460
489
|
|
|
461
|
-
|
|
462
|
-
info = client.server_info
|
|
463
|
-
puts "Available commands: #{info}"
|
|
490
|
+
### Content Blocks
|
|
464
491
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
492
|
+
| Type | Description |
|
|
493
|
+
|------|-------------|
|
|
494
|
+
| `TextBlock` | Text content with `text` attribute |
|
|
495
|
+
| `ThinkingBlock` | Claude's reasoning with `thinking` and `signature` |
|
|
496
|
+
| `ToolUseBlock` | Tool invocation with `id`, `name`, and `input` |
|
|
497
|
+
| `ToolResultBlock` | Tool execution result |
|
|
468
498
|
|
|
469
|
-
|
|
499
|
+
### Configuration
|
|
470
500
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
501
|
+
| Type | Description |
|
|
502
|
+
|------|-------------|
|
|
503
|
+
| `ClaudeAgentOptions` | Main configuration for queries and clients |
|
|
504
|
+
| `HookMatcher` | Hook configuration with matcher pattern |
|
|
505
|
+
| `PermissionResultAllow` | Permission callback result to allow tool use |
|
|
506
|
+
| `PermissionResultDeny` | Permission callback result to deny tool use |
|
|
475
507
|
|
|
476
508
|
## Error Handling
|
|
477
509
|
|
|
@@ -491,13 +523,16 @@ rescue ClaudeAgentSDK::CLIJSONDecodeError => e
|
|
|
491
523
|
end
|
|
492
524
|
```
|
|
493
525
|
|
|
494
|
-
Error
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
526
|
+
### Error Types
|
|
527
|
+
|
|
528
|
+
| Error | Description |
|
|
529
|
+
|-------|-------------|
|
|
530
|
+
| `ClaudeSDKError` | Base error for all SDK errors |
|
|
531
|
+
| `CLINotFoundError` | Claude Code not installed |
|
|
532
|
+
| `CLIConnectionError` | Connection issues |
|
|
533
|
+
| `ProcessError` | Process failed (includes `exit_code` and `stderr`) |
|
|
534
|
+
| `CLIJSONDecodeError` | JSON parsing issues |
|
|
535
|
+
| `MessageParseError` | Message parsing issues |
|
|
501
536
|
|
|
502
537
|
See [lib/claude_agent_sdk/errors.rb](lib/claude_agent_sdk/errors.rb) for all error types.
|
|
503
538
|
|
|
@@ -507,15 +542,15 @@ See the [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-co
|
|
|
507
542
|
|
|
508
543
|
## Examples
|
|
509
544
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
545
|
+
| Example | Description |
|
|
546
|
+
|---------|-------------|
|
|
547
|
+
| [examples/quick_start.rb](examples/quick_start.rb) | Basic `query()` usage with options |
|
|
548
|
+
| [examples/client_example.rb](examples/client_example.rb) | Interactive Client usage |
|
|
549
|
+
| [examples/streaming_input_example.rb](examples/streaming_input_example.rb) | Streaming input for multi-turn conversations |
|
|
550
|
+
| [examples/mcp_calculator.rb](examples/mcp_calculator.rb) | Custom tools with SDK MCP servers |
|
|
551
|
+
| [examples/mcp_resources_prompts_example.rb](examples/mcp_resources_prompts_example.rb) | MCP resources and prompts |
|
|
552
|
+
| [examples/hooks_example.rb](examples/hooks_example.rb) | Using hooks to control tool execution |
|
|
553
|
+
| [examples/permission_callback_example.rb](examples/permission_callback_example.rb) | Dynamic tool permission control |
|
|
519
554
|
|
|
520
555
|
## Development
|
|
521
556
|
|
|
@@ -54,7 +54,8 @@ module ClaudeAgentSDK
|
|
|
54
54
|
AssistantMessage.new(
|
|
55
55
|
content: content_blocks,
|
|
56
56
|
model: data.dig(:message, :model),
|
|
57
|
-
parent_tool_use_id: data[:parent_tool_use_id]
|
|
57
|
+
parent_tool_use_id: data[:parent_tool_use_id],
|
|
58
|
+
error: data[:error] # authentication_failed, billing_error, rate_limit, invalid_request, server_error, unknown
|
|
58
59
|
)
|
|
59
60
|
end
|
|
60
61
|
|
|
@@ -75,7 +76,8 @@ module ClaudeAgentSDK
|
|
|
75
76
|
session_id: data[:session_id],
|
|
76
77
|
total_cost_usd: data[:total_cost_usd],
|
|
77
78
|
usage: data[:usage],
|
|
78
|
-
result: data[:result]
|
|
79
|
+
result: data[:result],
|
|
80
|
+
structured_output: data[:structured_output] # Structured output when output_format is specified
|
|
79
81
|
)
|
|
80
82
|
end
|
|
81
83
|
|
|
@@ -217,16 +217,73 @@ module ClaudeAgentSDK
|
|
|
217
217
|
callback = @hook_callbacks[callback_id]
|
|
218
218
|
raise "No hook callback found for ID: #{callback_id}" unless callback
|
|
219
219
|
|
|
220
|
+
# Parse input data into typed HookInput object
|
|
221
|
+
input_data = request_data[:input] || {}
|
|
222
|
+
hook_input = parse_hook_input(input_data)
|
|
223
|
+
|
|
224
|
+
# Create typed HookContext
|
|
225
|
+
context = HookContext.new(signal: nil)
|
|
226
|
+
|
|
220
227
|
hook_output = callback.call(
|
|
221
|
-
|
|
228
|
+
hook_input,
|
|
222
229
|
request_data[:tool_use_id],
|
|
223
|
-
|
|
230
|
+
context
|
|
224
231
|
)
|
|
225
232
|
|
|
226
233
|
# Convert Ruby-safe field names to CLI-expected names
|
|
227
234
|
convert_hook_output_for_cli(hook_output)
|
|
228
235
|
end
|
|
229
236
|
|
|
237
|
+
def parse_hook_input(input_data)
|
|
238
|
+
event_name = input_data[:hook_event_name] || input_data['hook_event_name']
|
|
239
|
+
base_args = {
|
|
240
|
+
session_id: input_data[:session_id],
|
|
241
|
+
transcript_path: input_data[:transcript_path],
|
|
242
|
+
cwd: input_data[:cwd],
|
|
243
|
+
permission_mode: input_data[:permission_mode]
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
case event_name
|
|
247
|
+
when 'PreToolUse'
|
|
248
|
+
PreToolUseHookInput.new(
|
|
249
|
+
tool_name: input_data[:tool_name],
|
|
250
|
+
tool_input: input_data[:tool_input],
|
|
251
|
+
**base_args
|
|
252
|
+
)
|
|
253
|
+
when 'PostToolUse'
|
|
254
|
+
PostToolUseHookInput.new(
|
|
255
|
+
tool_name: input_data[:tool_name],
|
|
256
|
+
tool_input: input_data[:tool_input],
|
|
257
|
+
tool_response: input_data[:tool_response],
|
|
258
|
+
**base_args
|
|
259
|
+
)
|
|
260
|
+
when 'UserPromptSubmit'
|
|
261
|
+
UserPromptSubmitHookInput.new(
|
|
262
|
+
prompt: input_data[:prompt],
|
|
263
|
+
**base_args
|
|
264
|
+
)
|
|
265
|
+
when 'Stop'
|
|
266
|
+
StopHookInput.new(
|
|
267
|
+
stop_hook_active: input_data[:stop_hook_active],
|
|
268
|
+
**base_args
|
|
269
|
+
)
|
|
270
|
+
when 'SubagentStop'
|
|
271
|
+
SubagentStopHookInput.new(
|
|
272
|
+
stop_hook_active: input_data[:stop_hook_active],
|
|
273
|
+
**base_args
|
|
274
|
+
)
|
|
275
|
+
when 'PreCompact'
|
|
276
|
+
PreCompactHookInput.new(
|
|
277
|
+
trigger: input_data[:trigger],
|
|
278
|
+
custom_instructions: input_data[:custom_instructions],
|
|
279
|
+
**base_args
|
|
280
|
+
)
|
|
281
|
+
else
|
|
282
|
+
# Return base input for unknown event types
|
|
283
|
+
BaseHookInput.new(**base_args)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
230
287
|
def handle_mcp_message(request_data)
|
|
231
288
|
server_name = request_data[:server_name]
|
|
232
289
|
mcp_message = request_data[:message]
|
|
@@ -238,6 +295,13 @@ module ClaudeAgentSDK
|
|
|
238
295
|
end
|
|
239
296
|
|
|
240
297
|
def convert_hook_output_for_cli(hook_output)
|
|
298
|
+
# Handle typed output objects
|
|
299
|
+
if hook_output.respond_to?(:to_h) && !hook_output.is_a?(Hash)
|
|
300
|
+
return hook_output.to_h
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
return {} unless hook_output.is_a?(Hash)
|
|
304
|
+
|
|
241
305
|
# Convert Ruby hash with symbol keys to CLI format
|
|
242
306
|
# Handle special keywords that might be Ruby-safe versions
|
|
243
307
|
converted = {}
|
|
@@ -245,9 +309,21 @@ module ClaudeAgentSDK
|
|
|
245
309
|
converted_key = case key
|
|
246
310
|
when :async_, 'async_' then 'async'
|
|
247
311
|
when :continue_, 'continue_' then 'continue'
|
|
248
|
-
|
|
312
|
+
when :hook_specific_output then 'hookSpecificOutput'
|
|
313
|
+
when :suppress_output then 'suppressOutput'
|
|
314
|
+
when :stop_reason then 'stopReason'
|
|
315
|
+
when :system_message then 'systemMessage'
|
|
316
|
+
when :async_timeout then 'asyncTimeout'
|
|
317
|
+
else key.to_s
|
|
249
318
|
end
|
|
250
|
-
|
|
319
|
+
|
|
320
|
+
# Recursively convert nested objects
|
|
321
|
+
converted_value = if value.respond_to?(:to_h) && !value.is_a?(Hash)
|
|
322
|
+
value.to_h
|
|
323
|
+
else
|
|
324
|
+
value
|
|
325
|
+
end
|
|
326
|
+
converted[converted_key] = converted_value
|
|
251
327
|
end
|
|
252
328
|
converted
|
|
253
329
|
end
|
|
@@ -75,12 +75,33 @@ module ClaudeAgentSDK
|
|
|
75
75
|
cmd.concat(['--max-turns', @options.max_turns.to_s]) if @options.max_turns
|
|
76
76
|
cmd.concat(['--disallowedTools', @options.disallowed_tools.join(',')]) unless @options.disallowed_tools.empty?
|
|
77
77
|
cmd.concat(['--model', @options.model]) if @options.model
|
|
78
|
+
cmd.concat(['--fallback-model', @options.fallback_model]) if @options.fallback_model
|
|
78
79
|
cmd.concat(['--permission-prompt-tool', @options.permission_prompt_tool_name]) if @options.permission_prompt_tool_name
|
|
79
80
|
cmd.concat(['--permission-mode', @options.permission_mode]) if @options.permission_mode
|
|
80
81
|
cmd << '--continue' if @options.continue_conversation
|
|
81
82
|
cmd.concat(['--resume', @options.resume]) if @options.resume
|
|
82
83
|
cmd.concat(['--settings', @options.settings]) if @options.settings
|
|
83
84
|
|
|
85
|
+
# New options to match Python SDK
|
|
86
|
+
cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
|
|
87
|
+
# Note: max_thinking_tokens is stored in options but not yet supported by Claude CLI
|
|
88
|
+
|
|
89
|
+
# JSON schema for structured output
|
|
90
|
+
# Accepts either:
|
|
91
|
+
# 1. Direct schema: { type: 'object', properties: {...} }
|
|
92
|
+
# 2. Wrapped format: { type: 'json_schema', schema: {...} }
|
|
93
|
+
if @options.output_format
|
|
94
|
+
schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == 'json_schema'
|
|
95
|
+
@options.output_format[:schema]
|
|
96
|
+
elsif @options.output_format.is_a?(Hash) && @options.output_format['type'] == 'json_schema'
|
|
97
|
+
@options.output_format['schema']
|
|
98
|
+
else
|
|
99
|
+
@options.output_format
|
|
100
|
+
end
|
|
101
|
+
schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
|
|
102
|
+
cmd.concat(['--json-schema', schema_json])
|
|
103
|
+
end
|
|
104
|
+
|
|
84
105
|
# Add directories
|
|
85
106
|
@options.add_dirs.each do |dir|
|
|
86
107
|
cmd.concat(['--add-dir', dir.to_s])
|
|
@@ -121,6 +142,14 @@ module ClaudeAgentSDK
|
|
|
121
142
|
cmd.concat(['--agents', JSON.generate(agents_dict)])
|
|
122
143
|
end
|
|
123
144
|
|
|
145
|
+
# Plugins
|
|
146
|
+
if @options.plugins && !@options.plugins.empty?
|
|
147
|
+
plugins_config = @options.plugins.map do |plugin|
|
|
148
|
+
plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
|
|
149
|
+
end
|
|
150
|
+
cmd.concat(['--plugins', JSON.generate(plugins_config)])
|
|
151
|
+
end
|
|
152
|
+
|
|
124
153
|
# Setting sources
|
|
125
154
|
sources_value = @options.setting_sources ? @options.setting_sources.join(',') : ''
|
|
126
155
|
cmd.concat(['--setting-sources', sources_value])
|
|
@@ -159,7 +188,7 @@ module ClaudeAgentSDK
|
|
|
159
188
|
process_env['PWD'] = @cwd.to_s if @cwd
|
|
160
189
|
|
|
161
190
|
# Determine stderr handling
|
|
162
|
-
should_pipe_stderr = @options.stderr || @options.extra_args.key?('debug-to-stderr')
|
|
191
|
+
should_pipe_stderr = @options.stderr || @options.debug_stderr || @options.extra_args.key?('debug-to-stderr')
|
|
163
192
|
|
|
164
193
|
begin
|
|
165
194
|
# Start process using Open3
|
|
@@ -205,7 +234,17 @@ module ClaudeAgentSDK
|
|
|
205
234
|
line_str = line.chomp
|
|
206
235
|
next if line_str.empty?
|
|
207
236
|
|
|
237
|
+
# Call stderr callback if provided
|
|
208
238
|
@options.stderr&.call(line_str)
|
|
239
|
+
|
|
240
|
+
# Write to debug_stderr file/IO if provided
|
|
241
|
+
if @options.debug_stderr
|
|
242
|
+
if @options.debug_stderr.respond_to?(:puts)
|
|
243
|
+
@options.debug_stderr.puts(line_str)
|
|
244
|
+
elsif @options.debug_stderr.is_a?(String)
|
|
245
|
+
File.open(@options.debug_stderr, 'a') { |f| f.puts(line_str) }
|
|
246
|
+
end
|
|
247
|
+
end
|
|
209
248
|
end
|
|
210
249
|
rescue StandardError
|
|
211
250
|
# Ignore errors during stderr reading
|
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module ClaudeAgentSDK
|
|
4
|
+
# Type constants for permission modes
|
|
5
|
+
PERMISSION_MODES = %w[default acceptEdits plan bypassPermissions].freeze
|
|
6
|
+
|
|
7
|
+
# Type constants for setting sources
|
|
8
|
+
SETTING_SOURCES = %w[user project local].freeze
|
|
9
|
+
|
|
10
|
+
# Type constants for permission update destinations
|
|
11
|
+
PERMISSION_UPDATE_DESTINATIONS = %w[userSettings projectSettings localSettings session].freeze
|
|
12
|
+
|
|
13
|
+
# Type constants for permission behaviors
|
|
14
|
+
PERMISSION_BEHAVIORS = %w[allow deny ask].freeze
|
|
15
|
+
|
|
16
|
+
# Type constants for hook events
|
|
17
|
+
HOOK_EVENTS = %w[PreToolUse PostToolUse UserPromptSubmit Stop SubagentStop PreCompact].freeze
|
|
18
|
+
|
|
19
|
+
# Type constants for assistant message errors
|
|
20
|
+
ASSISTANT_MESSAGE_ERRORS = %w[authentication_failed billing_error rate_limit invalid_request server_error unknown].freeze
|
|
21
|
+
|
|
4
22
|
# Content Blocks
|
|
5
23
|
|
|
6
24
|
# Text content block
|
|
@@ -58,12 +76,13 @@ module ClaudeAgentSDK
|
|
|
58
76
|
|
|
59
77
|
# Assistant message with content blocks
|
|
60
78
|
class AssistantMessage
|
|
61
|
-
attr_accessor :content, :model, :parent_tool_use_id
|
|
79
|
+
attr_accessor :content, :model, :parent_tool_use_id, :error
|
|
62
80
|
|
|
63
|
-
def initialize(content:, model:, parent_tool_use_id: nil)
|
|
81
|
+
def initialize(content:, model:, parent_tool_use_id: nil, error: nil)
|
|
64
82
|
@content = content
|
|
65
83
|
@model = model
|
|
66
84
|
@parent_tool_use_id = parent_tool_use_id
|
|
85
|
+
@error = error # One of: authentication_failed, billing_error, rate_limit, invalid_request, server_error, unknown
|
|
67
86
|
end
|
|
68
87
|
end
|
|
69
88
|
|
|
@@ -80,10 +99,10 @@ module ClaudeAgentSDK
|
|
|
80
99
|
# Result message with cost and usage information
|
|
81
100
|
class ResultMessage
|
|
82
101
|
attr_accessor :subtype, :duration_ms, :duration_api_ms, :is_error,
|
|
83
|
-
:num_turns, :session_id, :total_cost_usd, :usage, :result
|
|
102
|
+
:num_turns, :session_id, :total_cost_usd, :usage, :result, :structured_output
|
|
84
103
|
|
|
85
104
|
def initialize(subtype:, duration_ms:, duration_api_ms:, is_error:,
|
|
86
|
-
num_turns:, session_id:, total_cost_usd: nil, usage: nil, result: nil)
|
|
105
|
+
num_turns:, session_id:, total_cost_usd: nil, usage: nil, result: nil, structured_output: nil)
|
|
87
106
|
@subtype = subtype
|
|
88
107
|
@duration_ms = duration_ms
|
|
89
108
|
@duration_api_ms = duration_api_ms
|
|
@@ -93,6 +112,7 @@ module ClaudeAgentSDK
|
|
|
93
112
|
@total_cost_usd = total_cost_usd
|
|
94
113
|
@usage = usage
|
|
95
114
|
@result = result
|
|
115
|
+
@structured_output = structured_output # Structured output when output_format is specified
|
|
96
116
|
end
|
|
97
117
|
end
|
|
98
118
|
|
|
@@ -201,11 +221,215 @@ module ClaudeAgentSDK
|
|
|
201
221
|
|
|
202
222
|
# Hook matcher configuration
|
|
203
223
|
class HookMatcher
|
|
204
|
-
attr_accessor :matcher, :hooks
|
|
224
|
+
attr_accessor :matcher, :hooks, :timeout
|
|
205
225
|
|
|
206
|
-
def initialize(matcher: nil, hooks: [])
|
|
226
|
+
def initialize(matcher: nil, hooks: [], timeout: nil)
|
|
207
227
|
@matcher = matcher
|
|
208
228
|
@hooks = hooks
|
|
229
|
+
@timeout = timeout # Timeout in seconds for hook execution
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Hook context passed to hook callbacks
|
|
234
|
+
class HookContext
|
|
235
|
+
attr_accessor :signal
|
|
236
|
+
|
|
237
|
+
def initialize(signal: nil)
|
|
238
|
+
@signal = signal
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Base hook input with common fields
|
|
243
|
+
class BaseHookInput
|
|
244
|
+
attr_accessor :session_id, :transcript_path, :cwd, :permission_mode
|
|
245
|
+
|
|
246
|
+
def initialize(session_id: nil, transcript_path: nil, cwd: nil, permission_mode: nil)
|
|
247
|
+
@session_id = session_id
|
|
248
|
+
@transcript_path = transcript_path
|
|
249
|
+
@cwd = cwd
|
|
250
|
+
@permission_mode = permission_mode
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# PreToolUse hook input
|
|
255
|
+
class PreToolUseHookInput < BaseHookInput
|
|
256
|
+
attr_accessor :hook_event_name, :tool_name, :tool_input
|
|
257
|
+
|
|
258
|
+
def initialize(hook_event_name: 'PreToolUse', tool_name: nil, tool_input: nil, **base_args)
|
|
259
|
+
super(**base_args)
|
|
260
|
+
@hook_event_name = hook_event_name
|
|
261
|
+
@tool_name = tool_name
|
|
262
|
+
@tool_input = tool_input
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# PostToolUse hook input
|
|
267
|
+
class PostToolUseHookInput < BaseHookInput
|
|
268
|
+
attr_accessor :hook_event_name, :tool_name, :tool_input, :tool_response
|
|
269
|
+
|
|
270
|
+
def initialize(hook_event_name: 'PostToolUse', tool_name: nil, tool_input: nil, tool_response: nil, **base_args)
|
|
271
|
+
super(**base_args)
|
|
272
|
+
@hook_event_name = hook_event_name
|
|
273
|
+
@tool_name = tool_name
|
|
274
|
+
@tool_input = tool_input
|
|
275
|
+
@tool_response = tool_response
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# UserPromptSubmit hook input
|
|
280
|
+
class UserPromptSubmitHookInput < BaseHookInput
|
|
281
|
+
attr_accessor :hook_event_name, :prompt
|
|
282
|
+
|
|
283
|
+
def initialize(hook_event_name: 'UserPromptSubmit', prompt: nil, **base_args)
|
|
284
|
+
super(**base_args)
|
|
285
|
+
@hook_event_name = hook_event_name
|
|
286
|
+
@prompt = prompt
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Stop hook input
|
|
291
|
+
class StopHookInput < BaseHookInput
|
|
292
|
+
attr_accessor :hook_event_name, :stop_hook_active
|
|
293
|
+
|
|
294
|
+
def initialize(hook_event_name: 'Stop', stop_hook_active: false, **base_args)
|
|
295
|
+
super(**base_args)
|
|
296
|
+
@hook_event_name = hook_event_name
|
|
297
|
+
@stop_hook_active = stop_hook_active
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# SubagentStop hook input
|
|
302
|
+
class SubagentStopHookInput < BaseHookInput
|
|
303
|
+
attr_accessor :hook_event_name, :stop_hook_active
|
|
304
|
+
|
|
305
|
+
def initialize(hook_event_name: 'SubagentStop', stop_hook_active: false, **base_args)
|
|
306
|
+
super(**base_args)
|
|
307
|
+
@hook_event_name = hook_event_name
|
|
308
|
+
@stop_hook_active = stop_hook_active
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# PreCompact hook input
|
|
313
|
+
class PreCompactHookInput < BaseHookInput
|
|
314
|
+
attr_accessor :hook_event_name, :trigger, :custom_instructions
|
|
315
|
+
|
|
316
|
+
def initialize(hook_event_name: 'PreCompact', trigger: nil, custom_instructions: nil, **base_args)
|
|
317
|
+
super(**base_args)
|
|
318
|
+
@hook_event_name = hook_event_name
|
|
319
|
+
@trigger = trigger
|
|
320
|
+
@custom_instructions = custom_instructions
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# PreToolUse hook specific output
|
|
325
|
+
class PreToolUseHookSpecificOutput
|
|
326
|
+
attr_accessor :hook_event_name, :permission_decision, :permission_decision_reason, :updated_input
|
|
327
|
+
|
|
328
|
+
def initialize(permission_decision: nil, permission_decision_reason: nil, updated_input: nil)
|
|
329
|
+
@hook_event_name = 'PreToolUse'
|
|
330
|
+
@permission_decision = permission_decision # 'allow', 'deny', or 'ask'
|
|
331
|
+
@permission_decision_reason = permission_decision_reason
|
|
332
|
+
@updated_input = updated_input
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def to_h
|
|
336
|
+
result = { hookEventName: @hook_event_name }
|
|
337
|
+
result[:permissionDecision] = @permission_decision if @permission_decision
|
|
338
|
+
result[:permissionDecisionReason] = @permission_decision_reason if @permission_decision_reason
|
|
339
|
+
result[:updatedInput] = @updated_input if @updated_input
|
|
340
|
+
result
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# PostToolUse hook specific output
|
|
345
|
+
class PostToolUseHookSpecificOutput
|
|
346
|
+
attr_accessor :hook_event_name, :additional_context
|
|
347
|
+
|
|
348
|
+
def initialize(additional_context: nil)
|
|
349
|
+
@hook_event_name = 'PostToolUse'
|
|
350
|
+
@additional_context = additional_context
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def to_h
|
|
354
|
+
result = { hookEventName: @hook_event_name }
|
|
355
|
+
result[:additionalContext] = @additional_context if @additional_context
|
|
356
|
+
result
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# UserPromptSubmit hook specific output
|
|
361
|
+
class UserPromptSubmitHookSpecificOutput
|
|
362
|
+
attr_accessor :hook_event_name, :additional_context
|
|
363
|
+
|
|
364
|
+
def initialize(additional_context: nil)
|
|
365
|
+
@hook_event_name = 'UserPromptSubmit'
|
|
366
|
+
@additional_context = additional_context
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def to_h
|
|
370
|
+
result = { hookEventName: @hook_event_name }
|
|
371
|
+
result[:additionalContext] = @additional_context if @additional_context
|
|
372
|
+
result
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# SessionStart hook specific output
|
|
377
|
+
class SessionStartHookSpecificOutput
|
|
378
|
+
attr_accessor :hook_event_name, :additional_context
|
|
379
|
+
|
|
380
|
+
def initialize(additional_context: nil)
|
|
381
|
+
@hook_event_name = 'SessionStart'
|
|
382
|
+
@additional_context = additional_context
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def to_h
|
|
386
|
+
result = { hookEventName: @hook_event_name }
|
|
387
|
+
result[:additionalContext] = @additional_context if @additional_context
|
|
388
|
+
result
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# Async hook JSON output
|
|
393
|
+
class AsyncHookJSONOutput
|
|
394
|
+
attr_accessor :async, :async_timeout
|
|
395
|
+
|
|
396
|
+
def initialize(async: true, async_timeout: nil)
|
|
397
|
+
@async = async
|
|
398
|
+
@async_timeout = async_timeout
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def to_h
|
|
402
|
+
result = { async: @async }
|
|
403
|
+
result[:asyncTimeout] = @async_timeout if @async_timeout
|
|
404
|
+
result
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Sync hook JSON output
|
|
409
|
+
class SyncHookJSONOutput
|
|
410
|
+
attr_accessor :continue, :suppress_output, :stop_reason, :decision,
|
|
411
|
+
:system_message, :reason, :hook_specific_output
|
|
412
|
+
|
|
413
|
+
def initialize(continue: true, suppress_output: false, stop_reason: nil, decision: nil,
|
|
414
|
+
system_message: nil, reason: nil, hook_specific_output: nil)
|
|
415
|
+
@continue = continue
|
|
416
|
+
@suppress_output = suppress_output
|
|
417
|
+
@stop_reason = stop_reason
|
|
418
|
+
@decision = decision
|
|
419
|
+
@system_message = system_message
|
|
420
|
+
@reason = reason
|
|
421
|
+
@hook_specific_output = hook_specific_output
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def to_h
|
|
425
|
+
result = { continue: @continue }
|
|
426
|
+
result[:suppressOutput] = @suppress_output if @suppress_output
|
|
427
|
+
result[:stopReason] = @stop_reason if @stop_reason
|
|
428
|
+
result[:decision] = @decision if @decision
|
|
429
|
+
result[:systemMessage] = @system_message if @system_message
|
|
430
|
+
result[:reason] = @reason if @reason
|
|
431
|
+
result[:hookSpecificOutput] = @hook_specific_output.to_h if @hook_specific_output
|
|
432
|
+
result
|
|
209
433
|
end
|
|
210
434
|
end
|
|
211
435
|
|
|
@@ -274,6 +498,20 @@ module ClaudeAgentSDK
|
|
|
274
498
|
end
|
|
275
499
|
end
|
|
276
500
|
|
|
501
|
+
# SDK Plugin configuration
|
|
502
|
+
class SdkPluginConfig
|
|
503
|
+
attr_accessor :type, :path
|
|
504
|
+
|
|
505
|
+
def initialize(path:)
|
|
506
|
+
@type = 'plugin'
|
|
507
|
+
@path = path
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
def to_h
|
|
511
|
+
{ type: @type, path: @path }
|
|
512
|
+
end
|
|
513
|
+
end
|
|
514
|
+
|
|
277
515
|
# Claude Agent Options for configuring queries
|
|
278
516
|
class ClaudeAgentOptions
|
|
279
517
|
attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
|
|
@@ -281,7 +519,10 @@ module ClaudeAgentSDK
|
|
|
281
519
|
:model, :permission_prompt_tool_name, :cwd, :cli_path, :settings,
|
|
282
520
|
:add_dirs, :env, :extra_args, :max_buffer_size, :stderr,
|
|
283
521
|
:can_use_tool, :hooks, :user, :include_partial_messages,
|
|
284
|
-
:fork_session, :agents, :setting_sources
|
|
522
|
+
:fork_session, :agents, :setting_sources,
|
|
523
|
+
# New options added to match Python SDK
|
|
524
|
+
:output_format, :max_budget_usd, :max_thinking_tokens,
|
|
525
|
+
:fallback_model, :plugins, :debug_stderr
|
|
285
526
|
|
|
286
527
|
def initialize(
|
|
287
528
|
allowed_tools: [],
|
|
@@ -308,7 +549,14 @@ module ClaudeAgentSDK
|
|
|
308
549
|
include_partial_messages: false,
|
|
309
550
|
fork_session: false,
|
|
310
551
|
agents: nil,
|
|
311
|
-
setting_sources: nil
|
|
552
|
+
setting_sources: nil,
|
|
553
|
+
# New options added to match Python SDK
|
|
554
|
+
output_format: nil,
|
|
555
|
+
max_budget_usd: nil,
|
|
556
|
+
max_thinking_tokens: nil,
|
|
557
|
+
fallback_model: nil,
|
|
558
|
+
plugins: nil,
|
|
559
|
+
debug_stderr: nil
|
|
312
560
|
)
|
|
313
561
|
@allowed_tools = allowed_tools
|
|
314
562
|
@system_prompt = system_prompt
|
|
@@ -335,6 +583,13 @@ module ClaudeAgentSDK
|
|
|
335
583
|
@fork_session = fork_session
|
|
336
584
|
@agents = agents
|
|
337
585
|
@setting_sources = setting_sources
|
|
586
|
+
# New options added to match Python SDK
|
|
587
|
+
@output_format = output_format # JSON schema for structured output
|
|
588
|
+
@max_budget_usd = max_budget_usd # Spending cap in dollars
|
|
589
|
+
@max_thinking_tokens = max_thinking_tokens # Extended thinking token budget
|
|
590
|
+
@fallback_model = fallback_model # Backup model if primary unavailable
|
|
591
|
+
@plugins = plugins # Array of SdkPluginConfig
|
|
592
|
+
@debug_stderr = debug_stderr # Debug output file object/path
|
|
338
593
|
end
|
|
339
594
|
|
|
340
595
|
def dup_with(**changes)
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-
|
|
10
|
+
date: 2025-12-11 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async
|