claude-agent-sdk 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1d1549408835720ec2fefea719ca9ccb948b0010bae0bbeb2955ac5e461fd8c
4
- data.tar.gz: 2b65f85b5ce0dec97e03395652ffeeadf6af4465a5346fc45d587ac20ac93642
3
+ metadata.gz: d4b8c3f8f0eefdfe38747d264f5d89148e1e589f12c200796dd2c37e821b3f61
4
+ data.tar.gz: 60252ef9c5f8679526a1f76b1be4216138e8bc7d91deb7bc1235b94d04a8481d
5
5
  SHA512:
6
- metadata.gz: 1de77e6f190cc6d474232801b53db8abdf8c239832266b304f6f48399317ab31f8b9298f8ed8af554d142d878476ba81ad712832a117fd01f8abffb798d50be1
7
- data.tar.gz: 9f9c6ad3bef6990c9e9efaea2f0b49c120b703508a74aaf152836032521b92143d07129f4930569a3bb5de97db1abac7f5d6e1868d9bcfb9bcf9a9fead5e1064
6
+ metadata.gz: a95797df469fa0acede9c2187d0e8dd9420582e856853552dcc4b0c09b7e4564ca7371478624a904090366d06880a2e7457f71910b88e815fbbe12d8f210f255
7
+ data.tar.gz: 77354e6852d2f86b060ac0e4948006ed1c28311f7481e4efa7cdc2f57864448ab0d656244559da43094cde1ac409ae25fd4aad13228640001b0e453d7d9fc753
data/CHANGELOG.md CHANGED
@@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-01-06
9
+
10
+ ### Added
11
+
12
+ #### File Checkpointing & Rewind
13
+ - `enable_file_checkpointing` option in `ClaudeAgentOptions` for enabling file state checkpointing
14
+ - `rewind_files(user_message_uuid)` method on `Query` and `Client` classes
15
+ - `uuid` field on `UserMessage` for tracking message identifiers for rewind support
16
+
17
+ #### Beta Features Support
18
+ - `SDK_BETAS` constant with available beta features (e.g., `"context-1m-2025-08-07"`)
19
+ - `betas` option in `ClaudeAgentOptions` for enabling beta features
20
+
21
+ #### Tools Configuration
22
+ - `tools` option for base tools selection (separate from `allowed_tools`)
23
+ - Supports array of tool names, empty array `[]`, or `ToolsPreset` object
24
+ - `ToolsPreset` class for preset-based tool configuration
25
+ - `append_allowed_tools` option to append tools to the allowed list
26
+
27
+ #### Sandbox Settings
28
+ - `SandboxSettings` class for isolated command execution configuration
29
+ - `SandboxNetworkConfig` class for network isolation settings
30
+ - `SandboxIgnoreViolations` class for configuring violation handling
31
+ - `sandbox` option in `ClaudeAgentOptions` for sandbox configuration
32
+ - Automatic merging of sandbox settings into the main settings JSON
33
+
34
+ #### Additional Types
35
+ - `SystemPromptPreset` class for preset-based system prompts
36
+
37
+ ### Technical Details
38
+ - All new CLI flags properly passed to Claude Code subprocess
39
+ - Sandbox settings merged into `--settings` JSON for CLI compatibility
40
+ - UserMessage UUID parsed from CLI output for rewind support
41
+
8
42
  ## [0.2.0] - 2025-10-17
9
43
 
10
44
  ### Changed
data/README.md CHANGED
@@ -15,6 +15,13 @@
15
15
  - [Custom Tools (SDK MCP Servers)](#custom-tools-sdk-mcp-servers)
16
16
  - [Hooks](#hooks)
17
17
  - [Permission Callbacks](#permission-callbacks)
18
+ - [Structured Output](#structured-output)
19
+ - [Budget Control](#budget-control)
20
+ - [Fallback Model](#fallback-model)
21
+ - [Beta Features](#beta-features)
22
+ - [Tools Configuration](#tools-configuration)
23
+ - [Sandbox Settings](#sandbox-settings)
24
+ - [File Checkpointing & Rewind](#file-checkpointing--rewind)
18
25
  - [Types](#types)
19
26
  - [Error Handling](#error-handling)
20
27
  - [Examples](#examples)
@@ -26,7 +33,7 @@
26
33
  Add this line to your application's Gemfile:
27
34
 
28
35
  ```ruby
29
- gem 'claude-agent-sdk', '~> 0.2.1'
36
+ gem 'claude-agent-sdk', '~> 0.4.0'
30
37
  ```
31
38
 
32
39
  And then execute:
@@ -473,40 +480,417 @@ end.wait
473
480
 
474
481
  For more examples, see [examples/permission_callback_example.rb](examples/permission_callback_example.rb).
475
482
 
483
+ ## Structured Output
484
+
485
+ Use `output_format` to get validated JSON responses matching a schema. The Claude CLI returns structured output via a `StructuredOutput` tool use block.
486
+
487
+ ```ruby
488
+ require 'claude_agent_sdk'
489
+ require 'json'
490
+
491
+ # Define a JSON schema
492
+ schema = {
493
+ type: 'object',
494
+ properties: {
495
+ name: { type: 'string' },
496
+ age: { type: 'integer' },
497
+ skills: { type: 'array', items: { type: 'string' } }
498
+ },
499
+ required: %w[name age skills]
500
+ }
501
+
502
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
503
+ output_format: { type: 'json_schema', schema: schema },
504
+ max_turns: 3
505
+ )
506
+
507
+ structured_data = nil
508
+
509
+ ClaudeAgentSDK.query(
510
+ prompt: "Create a profile for a software engineer",
511
+ options: options
512
+ ) do |message|
513
+ if message.is_a?(ClaudeAgentSDK::AssistantMessage)
514
+ message.content.each do |block|
515
+ # Structured output comes via StructuredOutput tool use
516
+ if block.is_a?(ClaudeAgentSDK::ToolUseBlock) && block.name == 'StructuredOutput'
517
+ structured_data = block.input
518
+ end
519
+ end
520
+ end
521
+ end
522
+
523
+ if structured_data
524
+ puts "Name: #{structured_data[:name]}"
525
+ puts "Age: #{structured_data[:age]}"
526
+ puts "Skills: #{structured_data[:skills].join(', ')}"
527
+ end
528
+ ```
529
+
530
+ For complete examples, see [examples/structured_output_example.rb](examples/structured_output_example.rb).
531
+
532
+ ## Budget Control
533
+
534
+ Use `max_budget_usd` to set a spending cap for your queries:
535
+
536
+ ```ruby
537
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
538
+ max_budget_usd: 0.10, # Cap at $0.10
539
+ max_turns: 3
540
+ )
541
+
542
+ ClaudeAgentSDK.query(prompt: "Explain recursion", options: options) do |message|
543
+ if message.is_a?(ClaudeAgentSDK::ResultMessage)
544
+ puts "Cost: $#{message.total_cost_usd}"
545
+ end
546
+ end
547
+ ```
548
+
549
+ For complete examples, see [examples/budget_control_example.rb](examples/budget_control_example.rb).
550
+
551
+ ## Fallback Model
552
+
553
+ Use `fallback_model` to specify a backup model if the primary is unavailable:
554
+
555
+ ```ruby
556
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
557
+ model: 'claude-sonnet-4-20250514',
558
+ fallback_model: 'claude-3-5-haiku-20241022'
559
+ )
560
+
561
+ ClaudeAgentSDK.query(prompt: "Hello", options: options) do |message|
562
+ if message.is_a?(ClaudeAgentSDK::AssistantMessage)
563
+ puts "Model used: #{message.model}"
564
+ end
565
+ end
566
+ ```
567
+
568
+ For complete examples, see [examples/fallback_model_example.rb](examples/fallback_model_example.rb).
569
+
570
+ ## Beta Features
571
+
572
+ Enable experimental features using the `betas` option:
573
+
574
+ ```ruby
575
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
576
+ betas: ['context-1m-2025-08-07'] # Extended context window
577
+ )
578
+
579
+ ClaudeAgentSDK.query(prompt: "Analyze this large document...", options: options) do |message|
580
+ puts message
581
+ end
582
+ ```
583
+
584
+ Available beta features are listed in the `SDK_BETAS` constant.
585
+
586
+ ## Tools Configuration
587
+
588
+ Configure base tools separately from allowed tools:
589
+
590
+ ```ruby
591
+ # Using an array of tool names
592
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
593
+ tools: ['Read', 'Edit', 'Bash'] # Base tools available
594
+ )
595
+
596
+ # Using a preset
597
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
598
+ tools: ClaudeAgentSDK::ToolsPreset.new(preset: 'claude_code')
599
+ )
600
+
601
+ # Appending to allowed tools
602
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
603
+ append_allowed_tools: ['Write', 'Bash']
604
+ )
605
+ ```
606
+
607
+ ## Sandbox Settings
608
+
609
+ Run commands in an isolated sandbox for additional security:
610
+
611
+ ```ruby
612
+ sandbox = ClaudeAgentSDK::SandboxSettings.new(
613
+ enabled: true,
614
+ auto_allow_bash_if_sandboxed: true,
615
+ network: ClaudeAgentSDK::SandboxNetworkConfig.new(
616
+ allow_local_binding: true
617
+ )
618
+ )
619
+
620
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
621
+ sandbox: sandbox,
622
+ permission_mode: 'acceptEdits'
623
+ )
624
+
625
+ ClaudeAgentSDK.query(prompt: "Run some commands", options: options) do |message|
626
+ puts message
627
+ end
628
+ ```
629
+
630
+ ## File Checkpointing & Rewind
631
+
632
+ Enable file checkpointing to revert file changes to a previous state:
633
+
634
+ ```ruby
635
+ require 'async'
636
+
637
+ Async do
638
+ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
639
+ enable_file_checkpointing: true,
640
+ permission_mode: 'acceptEdits'
641
+ )
642
+
643
+ client = ClaudeAgentSDK::Client.new(options: options)
644
+ client.connect
645
+
646
+ # Track user message UUIDs for potential rewind
647
+ user_message_uuids = []
648
+
649
+ # First query - create a file
650
+ client.query("Create a test.rb file with some code")
651
+ client.receive_response do |message|
652
+ # Process all message types as needed
653
+ case message
654
+ when ClaudeAgentSDK::UserMessage
655
+ # Capture UUID for rewind capability
656
+ user_message_uuids << message.uuid if message.uuid
657
+ when ClaudeAgentSDK::AssistantMessage
658
+ # Handle assistant responses
659
+ message.content.each do |block|
660
+ puts block.text if block.is_a?(ClaudeAgentSDK::TextBlock)
661
+ end
662
+ when ClaudeAgentSDK::ResultMessage
663
+ puts "Query completed (cost: $#{message.total_cost_usd})"
664
+ end
665
+ end
666
+
667
+ # Second query - modify the file
668
+ client.query("Modify the test.rb file to add error handling")
669
+ client.receive_response do |message|
670
+ user_message_uuids << message.uuid if message.is_a?(ClaudeAgentSDK::UserMessage) && message.uuid
671
+ end
672
+
673
+ # Rewind to the first checkpoint (undoes the second query's changes)
674
+ if user_message_uuids.first
675
+ puts "Rewinding to checkpoint: #{user_message_uuids.first}"
676
+ client.rewind_files(user_message_uuids.first)
677
+ end
678
+
679
+ client.disconnect
680
+ end
681
+ ```
682
+
683
+ > **Note:** The `uuid` field on `UserMessage` is populated by the CLI and represents checkpoint identifiers. Rewinding to a UUID restores file state to what it was at that point in the conversation.
684
+
476
685
  ## Types
477
686
 
478
- See [lib/claude_agent_sdk/types.rb](lib/claude_agent_sdk/types.rb) for complete type definitions:
687
+ See [lib/claude_agent_sdk/types.rb](lib/claude_agent_sdk/types.rb) for complete type definitions.
479
688
 
480
689
  ### Message Types
481
690
 
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 |
691
+ ```ruby
692
+ # Union type of all possible messages
693
+ Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage
694
+ ```
489
695
 
490
- ### Content Blocks
696
+ #### UserMessage
491
697
 
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 |
698
+ User input message.
699
+
700
+ ```ruby
701
+ class UserMessage
702
+ attr_accessor :content, # String | Array<ContentBlock>
703
+ :uuid, # String | nil - Unique ID for rewind support
704
+ :parent_tool_use_id # String | nil
705
+ end
706
+ ```
707
+
708
+ #### AssistantMessage
709
+
710
+ Assistant response message with content blocks.
711
+
712
+ ```ruby
713
+ class AssistantMessage
714
+ attr_accessor :content, # Array<ContentBlock>
715
+ :model, # String
716
+ :parent_tool_use_id,# String | nil
717
+ :error # String | nil ('authentication_failed', 'billing_error', 'rate_limit', 'invalid_request', 'server_error', 'unknown')
718
+ end
719
+ ```
720
+
721
+ #### SystemMessage
722
+
723
+ System message with metadata.
724
+
725
+ ```ruby
726
+ class SystemMessage
727
+ attr_accessor :subtype, # String ('init', etc.)
728
+ :data # Hash
729
+ end
730
+ ```
731
+
732
+ #### ResultMessage
733
+
734
+ Final result message with cost and usage information.
735
+
736
+ ```ruby
737
+ class ResultMessage
738
+ attr_accessor :subtype, # String
739
+ :duration_ms, # Integer
740
+ :duration_api_ms, # Integer
741
+ :is_error, # Boolean
742
+ :num_turns, # Integer
743
+ :session_id, # String
744
+ :total_cost_usd, # Float | nil
745
+ :usage, # Hash | nil
746
+ :result, # String | nil (final text result)
747
+ :structured_output # Hash | nil (when using output_format)
748
+ end
749
+ ```
750
+
751
+ ### Content Block Types
752
+
753
+ ```ruby
754
+ # Union type of all content blocks
755
+ ContentBlock = TextBlock | ThinkingBlock | ToolUseBlock | ToolResultBlock
756
+ ```
757
+
758
+ #### TextBlock
498
759
 
499
- ### Configuration
760
+ Text content block.
761
+
762
+ ```ruby
763
+ class TextBlock
764
+ attr_accessor :text # String
765
+ end
766
+ ```
767
+
768
+ #### ThinkingBlock
769
+
770
+ Thinking content block (for models with extended thinking capability).
771
+
772
+ ```ruby
773
+ class ThinkingBlock
774
+ attr_accessor :thinking, # String
775
+ :signature # String
776
+ end
777
+ ```
778
+
779
+ #### ToolUseBlock
780
+
781
+ Tool use request block.
782
+
783
+ ```ruby
784
+ class ToolUseBlock
785
+ attr_accessor :id, # String
786
+ :name, # String
787
+ :input # Hash
788
+ end
789
+ ```
790
+
791
+ #### ToolResultBlock
792
+
793
+ Tool execution result block.
794
+
795
+ ```ruby
796
+ class ToolResultBlock
797
+ attr_accessor :tool_use_id, # String
798
+ :content, # String | Array<Hash> | nil
799
+ :is_error # Boolean | nil
800
+ end
801
+ ```
802
+
803
+ ### Error Types
804
+
805
+ ```ruby
806
+ # Base exception class for all SDK errors
807
+ class ClaudeSDKError < StandardError; end
808
+
809
+ # Raised when Claude Code CLI is not found
810
+ class CLINotFoundError < CLIConnectionError
811
+ # @param message [String] Error message (default: "Claude Code not found")
812
+ # @param cli_path [String, nil] Optional path to the CLI that was not found
813
+ end
814
+
815
+ # Raised when connection to Claude Code fails
816
+ class CLIConnectionError < ClaudeSDKError; end
817
+
818
+ # Raised when the Claude Code process fails
819
+ class ProcessError < ClaudeSDKError
820
+ attr_reader :exit_code, # Integer | nil
821
+ :stderr # String | nil
822
+ end
823
+
824
+ # Raised when JSON parsing fails
825
+ class CLIJSONDecodeError < ClaudeSDKError
826
+ attr_reader :line, # String - The line that failed to parse
827
+ :original_error # Exception - The original JSON decode exception
828
+ end
829
+
830
+ # Raised when message parsing fails
831
+ class MessageParseError < ClaudeSDKError
832
+ attr_reader :data # Hash | nil
833
+ end
834
+ ```
835
+
836
+ ### Configuration Types
500
837
 
501
838
  | Type | Description |
502
839
  |------|-------------|
503
840
  | `ClaudeAgentOptions` | Main configuration for queries and clients |
504
- | `HookMatcher` | Hook configuration with matcher pattern |
841
+ | `HookMatcher` | Hook configuration with matcher pattern and timeout |
505
842
  | `PermissionResultAllow` | Permission callback result to allow tool use |
506
843
  | `PermissionResultDeny` | Permission callback result to deny tool use |
844
+ | `AgentDefinition` | Agent definition with description, prompt, tools, model |
845
+ | `McpStdioServerConfig` | MCP server config for stdio transport |
846
+ | `McpSSEServerConfig` | MCP server config for SSE transport |
847
+ | `McpHttpServerConfig` | MCP server config for HTTP transport |
848
+ | `SdkPluginConfig` | SDK plugin configuration |
849
+ | `SandboxSettings` | Sandbox settings for isolated command execution |
850
+ | `SandboxNetworkConfig` | Network configuration for sandbox |
851
+ | `SandboxIgnoreViolations` | Configure which sandbox violations to ignore |
852
+ | `SystemPromptPreset` | System prompt preset configuration |
853
+ | `ToolsPreset` | Tools preset configuration for base tools selection |
854
+
855
+ ### Constants
856
+
857
+ | Constant | Description |
858
+ |----------|-------------|
859
+ | `SDK_BETAS` | Available beta features (e.g., `"context-1m-2025-08-07"`) |
860
+ | `PERMISSION_MODES` | Available permission modes |
861
+ | `SETTING_SOURCES` | Available setting sources |
862
+ | `HOOK_EVENTS` | Available hook events |
863
+ | `ASSISTANT_MESSAGE_ERRORS` | Possible error types in AssistantMessage |
507
864
 
508
865
  ## Error Handling
509
866
 
867
+ ### AssistantMessage Errors
868
+
869
+ `AssistantMessage` includes an `error` field for API-level errors:
870
+
871
+ ```ruby
872
+ ClaudeAgentSDK.query(prompt: "Hello") do |message|
873
+ if message.is_a?(ClaudeAgentSDK::AssistantMessage) && message.error
874
+ case message.error
875
+ when 'rate_limit'
876
+ puts "Rate limited - retry after delay"
877
+ when 'authentication_failed'
878
+ puts "Check your API key"
879
+ when 'billing_error'
880
+ puts "Check your billing status"
881
+ when 'invalid_request'
882
+ puts "Invalid request format"
883
+ when 'server_error'
884
+ puts "Server error - retry later"
885
+ end
886
+ end
887
+ end
888
+ ```
889
+
890
+ For complete examples, see [examples/error_handling_example.rb](examples/error_handling_example.rb).
891
+
892
+ ### Exception Handling
893
+
510
894
  ```ruby
511
895
  require 'claude_agent_sdk'
512
896
 
@@ -551,6 +935,12 @@ See the [Claude Code documentation](https://docs.anthropic.com/en/docs/claude-co
551
935
  | [examples/mcp_resources_prompts_example.rb](examples/mcp_resources_prompts_example.rb) | MCP resources and prompts |
552
936
  | [examples/hooks_example.rb](examples/hooks_example.rb) | Using hooks to control tool execution |
553
937
  | [examples/permission_callback_example.rb](examples/permission_callback_example.rb) | Dynamic tool permission control |
938
+ | [examples/structured_output_example.rb](examples/structured_output_example.rb) | JSON schema structured output |
939
+ | [examples/budget_control_example.rb](examples/budget_control_example.rb) | Budget control with `max_budget_usd` |
940
+ | [examples/fallback_model_example.rb](examples/fallback_model_example.rb) | Fallback model configuration |
941
+ | [examples/advanced_hooks_example.rb](examples/advanced_hooks_example.rb) | Typed hook inputs/outputs |
942
+ | [examples/error_handling_example.rb](examples/error_handling_example.rb) | Error handling with `AssistantMessage.error` |
943
+ | [examples/extended_thinking_example.rb](examples/extended_thinking_example.rb) | Extended thinking (API parity) |
554
944
 
555
945
  ## Development
556
946
 
@@ -32,6 +32,7 @@ module ClaudeAgentSDK
32
32
 
33
33
  def self.parse_user_message(data)
34
34
  parent_tool_use_id = data[:parent_tool_use_id]
35
+ uuid = data[:uuid] # UUID for rewind support
35
36
  message_data = data[:message]
36
37
  raise MessageParseError.new("Missing message field in user message", data: data) unless message_data
37
38
 
@@ -40,9 +41,9 @@ module ClaudeAgentSDK
40
41
 
41
42
  if content.is_a?(Array)
42
43
  content_blocks = content.map { |block| parse_content_block(block) }
43
- UserMessage.new(content: content_blocks, parent_tool_use_id: parent_tool_use_id)
44
+ UserMessage.new(content: content_blocks, uuid: uuid, parent_tool_use_id: parent_tool_use_id)
44
45
  else
45
- UserMessage.new(content: content, parent_tool_use_id: parent_tool_use_id)
46
+ UserMessage.new(content: content, uuid: uuid, parent_tool_use_id: parent_tool_use_id)
46
47
  end
47
48
  end
48
49
 
@@ -551,6 +551,17 @@ module ClaudeAgentSDK
551
551
  })
552
552
  end
553
553
 
554
+ # Rewind files to a previous checkpoint (v0.1.15+)
555
+ # Restores file state to what it was at the given user message
556
+ # Requires enable_file_checkpointing to be true in options
557
+ # @param user_message_uuid [String] The UUID of the UserMessage to rewind to
558
+ def rewind_files(user_message_uuid)
559
+ send_control_request({
560
+ subtype: 'rewind_files',
561
+ userMessageUuid: user_message_uuid
562
+ })
563
+ end
564
+
554
565
  # Stream input messages to transport
555
566
  def stream_input(stream)
556
567
  stream.each do |message|
@@ -80,12 +80,77 @@ module ClaudeAgentSDK
80
80
  cmd.concat(['--permission-mode', @options.permission_mode]) if @options.permission_mode
81
81
  cmd << '--continue' if @options.continue_conversation
82
82
  cmd.concat(['--resume', @options.resume]) if @options.resume
83
- cmd.concat(['--settings', @options.settings]) if @options.settings
84
83
 
85
- # New options to match Python SDK
84
+ # Settings handling with sandbox merge
85
+ # Sandbox settings are merged into the main settings JSON
86
+ if @options.settings || @options.sandbox
87
+ settings_hash = {}
88
+ settings_is_path = false
89
+
90
+ # Parse existing settings if provided
91
+ if @options.settings
92
+ if @options.settings.is_a?(String)
93
+ begin
94
+ settings_hash = JSON.parse(@options.settings)
95
+ rescue JSON::ParserError
96
+ # If not valid JSON, treat as file path and pass as-is
97
+ settings_is_path = true
98
+ cmd.concat(['--settings', @options.settings])
99
+ if @options.sandbox
100
+ warn "Warning: Cannot merge sandbox settings when settings is a file path. " \
101
+ "Sandbox settings will be ignored. Use a Hash or JSON string for settings " \
102
+ "to enable sandbox merging."
103
+ end
104
+ end
105
+ elsif @options.settings.is_a?(Hash)
106
+ settings_hash = @options.settings.dup
107
+ end
108
+ end
109
+
110
+ # Merge sandbox settings if provided (only when settings is not a file path)
111
+ if !settings_is_path && @options.sandbox
112
+ sandbox_hash = if @options.sandbox.is_a?(SandboxSettings)
113
+ @options.sandbox.to_h
114
+ else
115
+ @options.sandbox
116
+ end
117
+ settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
118
+ end
119
+
120
+ # Output merged settings (only when settings is not a file path)
121
+ if !settings_is_path && !settings_hash.empty?
122
+ cmd.concat(['--settings', JSON.generate(settings_hash)])
123
+ end
124
+ end
125
+
126
+ # Budget limit option
86
127
  cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
87
128
  # Note: max_thinking_tokens is stored in options but not yet supported by Claude CLI
88
129
 
130
+ # Betas option for enabling experimental features
131
+ if @options.betas && !@options.betas.empty?
132
+ cmd.concat(['--betas', @options.betas.join(',')])
133
+ end
134
+
135
+ # Tools option for base tools selection
136
+ if @options.tools
137
+ if @options.tools.is_a?(Array)
138
+ cmd.concat(['--tools', @options.tools.join(',')])
139
+ elsif @options.tools.is_a?(ToolsPreset)
140
+ cmd.concat(['--tools', JSON.generate(@options.tools.to_h)])
141
+ elsif @options.tools.is_a?(Hash)
142
+ cmd.concat(['--tools', JSON.generate(@options.tools)])
143
+ end
144
+ end
145
+
146
+ # Append allowed tools option
147
+ if @options.append_allowed_tools && !@options.append_allowed_tools.empty?
148
+ cmd.concat(['--append-allowed-tools', @options.append_allowed_tools.join(',')])
149
+ end
150
+
151
+ # File checkpointing for rewind support
152
+ cmd << '--enable-file-checkpointing' if @options.enable_file_checkpointing
153
+
89
154
  # JSON schema for structured output
90
155
  # Accepts either:
91
156
  # 1. Direct schema: { type: 'object', properties: {...} }
@@ -19,6 +19,10 @@ module ClaudeAgentSDK
19
19
  # Type constants for assistant message errors
20
20
  ASSISTANT_MESSAGE_ERRORS = %w[authentication_failed billing_error rate_limit invalid_request server_error unknown].freeze
21
21
 
22
+ # Type constants for SDK beta features
23
+ # Available beta features that can be enabled via the betas option
24
+ SDK_BETAS = %w[context-1m-2025-08-07].freeze
25
+
22
26
  # Content Blocks
23
27
 
24
28
  # Text content block
@@ -66,10 +70,11 @@ module ClaudeAgentSDK
66
70
 
67
71
  # User message
68
72
  class UserMessage
69
- attr_accessor :content, :parent_tool_use_id
73
+ attr_accessor :content, :uuid, :parent_tool_use_id
70
74
 
71
- def initialize(content:, parent_tool_use_id: nil)
75
+ def initialize(content:, uuid: nil, parent_tool_use_id: nil)
72
76
  @content = content
77
+ @uuid = uuid # Unique identifier for rewind support
73
78
  @parent_tool_use_id = parent_tool_use_id
74
79
  end
75
80
  end
@@ -512,6 +517,125 @@ module ClaudeAgentSDK
512
517
  end
513
518
  end
514
519
 
520
+ # Sandbox network configuration
521
+ class SandboxNetworkConfig
522
+ attr_accessor :allow_unix_sockets, :allow_all_unix_sockets, :allow_local_binding,
523
+ :http_proxy_port, :socks_proxy_port
524
+
525
+ def initialize(
526
+ allow_unix_sockets: nil,
527
+ allow_all_unix_sockets: nil,
528
+ allow_local_binding: nil,
529
+ http_proxy_port: nil,
530
+ socks_proxy_port: nil
531
+ )
532
+ @allow_unix_sockets = allow_unix_sockets
533
+ @allow_all_unix_sockets = allow_all_unix_sockets
534
+ @allow_local_binding = allow_local_binding
535
+ @http_proxy_port = http_proxy_port
536
+ @socks_proxy_port = socks_proxy_port
537
+ end
538
+
539
+ def to_h
540
+ result = {}
541
+ result[:allowUnixSockets] = @allow_unix_sockets unless @allow_unix_sockets.nil?
542
+ result[:allowAllUnixSockets] = @allow_all_unix_sockets unless @allow_all_unix_sockets.nil?
543
+ result[:allowLocalBinding] = @allow_local_binding unless @allow_local_binding.nil?
544
+ result[:httpProxyPort] = @http_proxy_port if @http_proxy_port
545
+ result[:socksProxyPort] = @socks_proxy_port if @socks_proxy_port
546
+ result
547
+ end
548
+ end
549
+
550
+ # Sandbox ignore violations configuration
551
+ class SandboxIgnoreViolations
552
+ attr_accessor :file, :network
553
+
554
+ def initialize(file: nil, network: nil)
555
+ @file = file # Array of file paths to ignore
556
+ @network = network # Array of network patterns to ignore
557
+ end
558
+
559
+ def to_h
560
+ result = {}
561
+ result[:file] = @file if @file
562
+ result[:network] = @network if @network
563
+ result
564
+ end
565
+ end
566
+
567
+ # Sandbox settings for isolated command execution
568
+ class SandboxSettings
569
+ attr_accessor :enabled, :auto_allow_bash_if_sandboxed, :excluded_commands,
570
+ :allow_unsandboxed_commands, :network, :ignore_violations,
571
+ :enable_weaker_nested_sandbox
572
+
573
+ def initialize(
574
+ enabled: nil,
575
+ auto_allow_bash_if_sandboxed: nil,
576
+ excluded_commands: nil,
577
+ allow_unsandboxed_commands: nil,
578
+ network: nil,
579
+ ignore_violations: nil,
580
+ enable_weaker_nested_sandbox: nil
581
+ )
582
+ @enabled = enabled
583
+ @auto_allow_bash_if_sandboxed = auto_allow_bash_if_sandboxed
584
+ @excluded_commands = excluded_commands # Array of commands to exclude
585
+ @allow_unsandboxed_commands = allow_unsandboxed_commands
586
+ @network = network # SandboxNetworkConfig instance
587
+ @ignore_violations = ignore_violations # SandboxIgnoreViolations instance
588
+ @enable_weaker_nested_sandbox = enable_weaker_nested_sandbox
589
+ end
590
+
591
+ def to_h
592
+ result = {}
593
+ result[:enabled] = @enabled unless @enabled.nil?
594
+ result[:autoAllowBashIfSandboxed] = @auto_allow_bash_if_sandboxed unless @auto_allow_bash_if_sandboxed.nil?
595
+ result[:excludedCommands] = @excluded_commands if @excluded_commands
596
+ result[:allowUnsandboxedCommands] = @allow_unsandboxed_commands unless @allow_unsandboxed_commands.nil?
597
+ if @network
598
+ result[:network] = @network.is_a?(SandboxNetworkConfig) ? @network.to_h : @network
599
+ end
600
+ if @ignore_violations
601
+ result[:ignoreViolations] = @ignore_violations.is_a?(SandboxIgnoreViolations) ? @ignore_violations.to_h : @ignore_violations
602
+ end
603
+ result[:enableWeakerNestedSandbox] = @enable_weaker_nested_sandbox unless @enable_weaker_nested_sandbox.nil?
604
+ result
605
+ end
606
+ end
607
+
608
+ # System prompt preset configuration
609
+ class SystemPromptPreset
610
+ attr_accessor :type, :preset, :append
611
+
612
+ def initialize(preset:, append: nil)
613
+ @type = 'preset'
614
+ @preset = preset
615
+ @append = append
616
+ end
617
+
618
+ def to_h
619
+ result = { type: @type, preset: @preset }
620
+ result[:append] = @append if @append
621
+ result
622
+ end
623
+ end
624
+
625
+ # Tools preset configuration
626
+ class ToolsPreset
627
+ attr_accessor :type, :preset
628
+
629
+ def initialize(preset:)
630
+ @type = 'preset'
631
+ @preset = preset
632
+ end
633
+
634
+ def to_h
635
+ { type: @type, preset: @preset }
636
+ end
637
+ end
638
+
515
639
  # Claude Agent Options for configuring queries
516
640
  class ClaudeAgentOptions
517
641
  attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
@@ -520,9 +644,9 @@ module ClaudeAgentSDK
520
644
  :add_dirs, :env, :extra_args, :max_buffer_size, :stderr,
521
645
  :can_use_tool, :hooks, :user, :include_partial_messages,
522
646
  :fork_session, :agents, :setting_sources,
523
- # New options added to match Python SDK
524
647
  :output_format, :max_budget_usd, :max_thinking_tokens,
525
- :fallback_model, :plugins, :debug_stderr
648
+ :fallback_model, :plugins, :debug_stderr,
649
+ :betas, :tools, :sandbox, :enable_file_checkpointing, :append_allowed_tools
526
650
 
527
651
  def initialize(
528
652
  allowed_tools: [],
@@ -550,13 +674,17 @@ module ClaudeAgentSDK
550
674
  fork_session: false,
551
675
  agents: nil,
552
676
  setting_sources: nil,
553
- # New options added to match Python SDK
554
677
  output_format: nil,
555
678
  max_budget_usd: nil,
556
679
  max_thinking_tokens: nil,
557
680
  fallback_model: nil,
558
681
  plugins: nil,
559
- debug_stderr: nil
682
+ debug_stderr: nil,
683
+ betas: nil,
684
+ tools: nil,
685
+ sandbox: nil,
686
+ enable_file_checkpointing: false,
687
+ append_allowed_tools: nil
560
688
  )
561
689
  @allowed_tools = allowed_tools
562
690
  @system_prompt = system_prompt
@@ -583,13 +711,17 @@ module ClaudeAgentSDK
583
711
  @fork_session = fork_session
584
712
  @agents = agents
585
713
  @setting_sources = setting_sources
586
- # New options added to match Python SDK
587
714
  @output_format = output_format # JSON schema for structured output
588
715
  @max_budget_usd = max_budget_usd # Spending cap in dollars
589
716
  @max_thinking_tokens = max_thinking_tokens # Extended thinking token budget
590
717
  @fallback_model = fallback_model # Backup model if primary unavailable
591
718
  @plugins = plugins # Array of SdkPluginConfig
592
719
  @debug_stderr = debug_stderr # Debug output file object/path
720
+ @betas = betas # Array of beta feature strings (e.g., ["context-1m-2025-08-07"])
721
+ @tools = tools # Base tools selection: Array, empty array [], or ToolsPreset
722
+ @sandbox = sandbox # SandboxSettings instance for isolated command execution
723
+ @enable_file_checkpointing = enable_file_checkpointing # Enable file checkpointing for rewind support
724
+ @append_allowed_tools = append_allowed_tools # Array of tools to append to allowed_tools
593
725
  end
594
726
 
595
727
  def dup_with(**changes)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -244,6 +244,15 @@ module ClaudeAgentSDK
244
244
  @query_handler.set_model(model)
245
245
  end
246
246
 
247
+ # Rewind files to a previous checkpoint (v0.1.15+)
248
+ # Restores file state to what it was at the given user message
249
+ # Requires enable_file_checkpointing to be true in options
250
+ # @param user_message_uuid [String] The UUID of the UserMessage to rewind to
251
+ def rewind_files(user_message_uuid)
252
+ raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
253
+ @query_handler.rewind_files(user_message_uuid)
254
+ end
255
+
247
256
  # Get server initialization info
248
257
  # @return [Hash, nil] Server info or nil
249
258
  def server_info
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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Community Contributors
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-11 00:00:00.000000000 Z
10
+ date: 2026-01-05 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: async