claude-agent-sdk 0.36.0 → 0.37.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/CHANGELOG.md +17 -0
- data/README.md +2 -2
- data/docs/client.md +26 -1
- data/docs/hooks-and-permissions.md +5 -3
- data/docs/mcp-servers.md +1 -2
- data/docs/sessions.md +81 -2
- data/docs/types.md +106 -4
- data/lib/claude_agent_sdk/cli_installer.rb +30 -3
- data/lib/claude_agent_sdk/command_builder.rb +109 -98
- data/lib/claude_agent_sdk/deprecation.rb +39 -0
- data/lib/claude_agent_sdk/fiber_boundary.rb +2 -0
- data/lib/claude_agent_sdk/instrumentation/otel.rb +15 -7
- data/lib/claude_agent_sdk/message_parser.rb +23 -9
- data/lib/claude_agent_sdk/observer.rb +2 -1
- data/lib/claude_agent_sdk/option_warnings.rb +2 -0
- data/lib/claude_agent_sdk/query.rb +50 -43
- data/lib/claude_agent_sdk/sdk_mcp_server.rb +22 -13
- data/lib/claude_agent_sdk/session_mutations.rb +20 -8
- data/lib/claude_agent_sdk/session_resume.rb +20 -11
- data/lib/claude_agent_sdk/session_store.rb +7 -3
- data/lib/claude_agent_sdk/session_summary.rb +4 -2
- data/lib/claude_agent_sdk/sessions.rb +8 -6
- data/lib/claude_agent_sdk/streaming.rb +1 -1
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +72 -40
- data/lib/claude_agent_sdk/testing/session_store_conformance.rb +14 -10
- data/lib/claude_agent_sdk/transcript_mirror_batcher.rb +2 -0
- data/lib/claude_agent_sdk/types/attributes.rb +271 -0
- data/lib/claude_agent_sdk/types/base.rb +320 -0
- data/lib/claude_agent_sdk/types/content_blocks.rb +57 -0
- data/lib/claude_agent_sdk/types/hooks.rb +640 -0
- data/lib/claude_agent_sdk/types/mcp.rb +232 -0
- data/lib/claude_agent_sdk/types/messages.rb +614 -0
- data/lib/claude_agent_sdk/types/option_values.rb +302 -0
- data/lib/claude_agent_sdk/types/options.rb +352 -0
- data/lib/claude_agent_sdk/types/permissions.rb +107 -0
- data/lib/claude_agent_sdk/types/sessions.rb +10 -0
- data/lib/claude_agent_sdk/types.rb +13 -2534
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +51 -17
- metadata +11 -1
data/lib/claude_agent_sdk.rb
CHANGED
|
@@ -29,9 +29,11 @@ require 'async'
|
|
|
29
29
|
require 'securerandom'
|
|
30
30
|
|
|
31
31
|
# Claude Agent SDK for Ruby
|
|
32
|
-
module ClaudeAgentSDK
|
|
32
|
+
module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry points (query, ask, sessions API) live on the root module
|
|
33
33
|
# The duck-typed observer surface probed by resolve_observers — implementing
|
|
34
34
|
# any one of these counts as an observer (see Observer's no-op defaults).
|
|
35
|
+
#
|
|
36
|
+
# @api private
|
|
35
37
|
OBSERVER_INTERFACE = %i[on_user_prompt on_message on_error on_close].freeze
|
|
36
38
|
|
|
37
39
|
# Resolve observers array: callables (Proc/lambda) are invoked to produce
|
|
@@ -117,7 +119,9 @@ module ClaudeAgentSDK
|
|
|
117
119
|
return options unless options.can_use_tool
|
|
118
120
|
|
|
119
121
|
# can_use_tool and permission_prompt_tool_name are mutually exclusive
|
|
120
|
-
|
|
122
|
+
if options.permission_prompt_tool_name
|
|
123
|
+
raise ArgumentError, 'can_use_tool callback cannot be used with permission_prompt_tool_name'
|
|
124
|
+
end
|
|
121
125
|
|
|
122
126
|
# Advisory: warn if other options shadow the callback. After the
|
|
123
127
|
# ArgumentError above so invalid configs raise, not warn.
|
|
@@ -621,6 +625,7 @@ module ClaudeAgentSDK
|
|
|
621
625
|
# Replay a local on-disk session transcript into a SessionStore (migration /
|
|
622
626
|
# gap-backfill). Keys under the on-disk project dir so the imported session is
|
|
623
627
|
# resumable via session_store + resume from the original cwd.
|
|
628
|
+
# @param batch_size [Integer] entries per SessionStore#append call (default 500)
|
|
624
629
|
# @raise [ArgumentError] if session_id is not a valid UUID
|
|
625
630
|
# @raise [Errno::ENOENT] if the session JSONL cannot be found
|
|
626
631
|
def self.import_session_to_store(session_id:, session_store:, directory: nil, include_subagents: true,
|
|
@@ -664,13 +669,17 @@ module ClaudeAgentSDK
|
|
|
664
669
|
# ClaudeAgentSDK.query(prompt: messages) do |message|
|
|
665
670
|
# puts message
|
|
666
671
|
# end
|
|
667
|
-
def self.query(prompt:, options: nil, transport: nil, &block)
|
|
672
|
+
def self.query(prompt:, options: nil, transport: nil, &block) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity -- one-shot lifecycle (validate, resume, connect, stream, teardown) kept linear
|
|
668
673
|
# Validate BEFORE the block-less enum_for return so a bad prompt fails at
|
|
669
674
|
# the call site, not on first iteration. Mirrors Client#query: a bare Hash
|
|
670
675
|
# responds to #each and would stream [key, value] pairs' to_s garbage to
|
|
671
676
|
# the CLI; nil/Integer would hang forever waiting for input.
|
|
672
|
-
|
|
673
|
-
|
|
677
|
+
if prompt.is_a?(Hash)
|
|
678
|
+
raise ArgumentError, 'prompt must be a String or an Enumerable of message Hashes/JSONL Strings (got Hash)'
|
|
679
|
+
end
|
|
680
|
+
unless prompt.is_a?(String) || prompt.respond_to?(:each)
|
|
681
|
+
raise ArgumentError, "prompt must be a String or respond to #each (got #{prompt.class})"
|
|
682
|
+
end
|
|
674
683
|
|
|
675
684
|
return enum_for(:query, prompt: prompt, options: options, transport: transport) unless block
|
|
676
685
|
|
|
@@ -690,9 +699,11 @@ module ClaudeAgentSDK
|
|
|
690
699
|
callback_wrapper = configured_options.callback_wrapper
|
|
691
700
|
ClaudeAgentSDK.check_inline_isolation(callback_scheduling)
|
|
692
701
|
|
|
693
|
-
|
|
702
|
+
if transport && !transport.respond_to?(:connect)
|
|
703
|
+
raise ArgumentError, 'transport must respond to #connect (see ClaudeAgentSDK::Transport)'
|
|
704
|
+
end
|
|
694
705
|
|
|
695
|
-
Async(&FiberBoundary.capture_otel_context do
|
|
706
|
+
Async(&FiberBoundary.capture_otel_context do # rubocop:disable Metrics/BlockLength -- the reactor task body of query()
|
|
696
707
|
materialized = nil
|
|
697
708
|
query_handler = nil
|
|
698
709
|
begin
|
|
@@ -705,7 +716,9 @@ module ClaudeAgentSDK
|
|
|
705
716
|
# env/--resume only apply to the CLI subprocess (Python parity:
|
|
706
717
|
# client.py skips materialization when a transport is supplied).
|
|
707
718
|
materialized = SessionResume.materialize_resume_session(configured_options)
|
|
708
|
-
|
|
719
|
+
if materialized
|
|
720
|
+
configured_options = SessionResume.apply_materialized_options(configured_options, materialized)
|
|
721
|
+
end
|
|
709
722
|
|
|
710
723
|
# Always use streaming mode with control protocol (matches Python
|
|
711
724
|
# SDK). This sends agents via initialize request instead of CLI
|
|
@@ -769,7 +782,7 @@ module ClaudeAgentSDK
|
|
|
769
782
|
parent_tool_use_id: nil,
|
|
770
783
|
session_id: ''
|
|
771
784
|
}
|
|
772
|
-
transport.write(JSON.generate(message)
|
|
785
|
+
transport.write("#{JSON.generate(message)}\n")
|
|
773
786
|
# Background-spawn so messages stream to the user block while stdin
|
|
774
787
|
# close waits (without timeout) for the first result; a synchronous
|
|
775
788
|
# call would defer all delivery until the turn completes (mirrors
|
|
@@ -780,8 +793,9 @@ module ClaudeAgentSDK
|
|
|
780
793
|
# here kept the root reactor alive forever when the read loop died
|
|
781
794
|
# while the user enumerator was still blocked (matches Python's
|
|
782
795
|
# query.spawn_task(query.stream_input(prompt))).
|
|
783
|
-
observed_prompt = ClaudeAgentSDK.observing_prompt_stream(
|
|
784
|
-
|
|
796
|
+
observed_prompt = ClaudeAgentSDK.observing_prompt_stream(
|
|
797
|
+
prompt, resolved_observers, scheduling: callback_scheduling, wrapper: callback_wrapper
|
|
798
|
+
)
|
|
785
799
|
query_handler.spawn_task { query_handler.stream_input(observed_prompt) }
|
|
786
800
|
end
|
|
787
801
|
|
|
@@ -916,7 +930,10 @@ module ClaudeAgentSDK
|
|
|
916
930
|
# }
|
|
917
931
|
# )
|
|
918
932
|
# client = ClaudeAgentSDK::Client.new(options: options)
|
|
919
|
-
class Client
|
|
933
|
+
class Client # rubocop:disable Metrics/ClassLength -- public session API: lifecycle, control methods and their Ruby aliases
|
|
934
|
+
# The session's control-protocol handler (nil until #connect).
|
|
935
|
+
#
|
|
936
|
+
# @api private
|
|
920
937
|
attr_reader :query_handler
|
|
921
938
|
|
|
922
939
|
# @param options [ClaudeAgentOptions, nil] Configuration options
|
|
@@ -983,8 +1000,12 @@ module ClaudeAgentSDK
|
|
|
983
1000
|
def connect(prompt = nil)
|
|
984
1001
|
return if @connected
|
|
985
1002
|
|
|
986
|
-
|
|
987
|
-
|
|
1003
|
+
if prompt.is_a?(Hash)
|
|
1004
|
+
raise ArgumentError, 'prompt must be a String or an Enumerable of message Hashes/JSONL Strings (got Hash)'
|
|
1005
|
+
end
|
|
1006
|
+
unless prompt.nil? || prompt.is_a?(String) || prompt.respond_to?(:each)
|
|
1007
|
+
raise ArgumentError, "prompt must be a String, an Enumerator, or nil (got #{prompt.class})"
|
|
1008
|
+
end
|
|
988
1009
|
|
|
989
1010
|
# Validate and configure permission settings
|
|
990
1011
|
configured_options = ClaudeAgentSDK.configure_can_use_tool(@options)
|
|
@@ -1049,7 +1070,9 @@ module ClaudeAgentSDK
|
|
|
1049
1070
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1050
1071
|
# A bare Hash responds to #each and would silently iterate [key, value]
|
|
1051
1072
|
# pairs (Python's async-for over a dict raises TypeError).
|
|
1052
|
-
|
|
1073
|
+
if prompt.is_a?(Hash)
|
|
1074
|
+
raise ArgumentError, 'prompt must be a String or an Enumerable of message Hashes/JSONL Strings (got Hash)'
|
|
1075
|
+
end
|
|
1053
1076
|
|
|
1054
1077
|
begin
|
|
1055
1078
|
if prompt.is_a?(String)
|
|
@@ -1139,6 +1162,7 @@ module ClaudeAgentSDK
|
|
|
1139
1162
|
# Send interrupt signal
|
|
1140
1163
|
def interrupt
|
|
1141
1164
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1165
|
+
|
|
1142
1166
|
@query_handler.interrupt
|
|
1143
1167
|
end
|
|
1144
1168
|
|
|
@@ -1146,6 +1170,7 @@ module ClaudeAgentSDK
|
|
|
1146
1170
|
# @param mode [String] Permission mode ('default', 'acceptEdits', 'bypassPermissions')
|
|
1147
1171
|
def set_permission_mode(mode)
|
|
1148
1172
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1173
|
+
|
|
1149
1174
|
@query_handler.set_permission_mode(mode)
|
|
1150
1175
|
end
|
|
1151
1176
|
|
|
@@ -1159,6 +1184,7 @@ module ClaudeAgentSDK
|
|
|
1159
1184
|
# @param model [String, nil] Model name or nil for default
|
|
1160
1185
|
def set_model(model)
|
|
1161
1186
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1187
|
+
|
|
1162
1188
|
@query_handler.set_model(model)
|
|
1163
1189
|
end
|
|
1164
1190
|
|
|
@@ -1171,6 +1197,7 @@ module ClaudeAgentSDK
|
|
|
1171
1197
|
# @param server_name [String] Name of the MCP server to reconnect
|
|
1172
1198
|
def reconnect_mcp_server(server_name)
|
|
1173
1199
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1200
|
+
|
|
1174
1201
|
@query_handler.reconnect_mcp_server(server_name)
|
|
1175
1202
|
end
|
|
1176
1203
|
|
|
@@ -1179,6 +1206,7 @@ module ClaudeAgentSDK
|
|
|
1179
1206
|
# @param enabled [Boolean] Whether to enable or disable
|
|
1180
1207
|
def toggle_mcp_server(server_name, enabled)
|
|
1181
1208
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1209
|
+
|
|
1182
1210
|
@query_handler.toggle_mcp_server(server_name, enabled)
|
|
1183
1211
|
end
|
|
1184
1212
|
|
|
@@ -1186,6 +1214,7 @@ module ClaudeAgentSDK
|
|
|
1186
1214
|
# @param task_id [String] The ID of the task to stop
|
|
1187
1215
|
def stop_task(task_id)
|
|
1188
1216
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1217
|
+
|
|
1189
1218
|
@query_handler.stop_task(task_id)
|
|
1190
1219
|
end
|
|
1191
1220
|
|
|
@@ -1211,6 +1240,7 @@ module ClaudeAgentSDK
|
|
|
1211
1240
|
# @raise [ArgumentError] if tool_use_id is neither nil nor a non-empty String
|
|
1212
1241
|
def background_tasks(tool_use_id: nil)
|
|
1213
1242
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1243
|
+
|
|
1214
1244
|
@query_handler.background_tasks(tool_use_id: tool_use_id)
|
|
1215
1245
|
end
|
|
1216
1246
|
|
|
@@ -1220,13 +1250,14 @@ module ClaudeAgentSDK
|
|
|
1220
1250
|
# @param user_message_uuid [String] The UUID of the UserMessage to rewind to
|
|
1221
1251
|
def rewind_files(user_message_uuid)
|
|
1222
1252
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1253
|
+
|
|
1223
1254
|
@query_handler.rewind_files(user_message_uuid)
|
|
1224
1255
|
end
|
|
1225
1256
|
|
|
1226
1257
|
# Get server initialization info
|
|
1227
1258
|
# @return [Hash, nil] Server info or nil
|
|
1228
1259
|
def server_info
|
|
1229
|
-
@query_handler&.
|
|
1260
|
+
@query_handler&.initialization_result
|
|
1230
1261
|
end
|
|
1231
1262
|
|
|
1232
1263
|
# Get a breakdown of current context window usage by category.
|
|
@@ -1235,6 +1266,7 @@ module ClaudeAgentSDK
|
|
|
1235
1266
|
# @return [Hash] Context usage response
|
|
1236
1267
|
def get_context_usage
|
|
1237
1268
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1269
|
+
|
|
1238
1270
|
@query_handler.get_context_usage
|
|
1239
1271
|
end
|
|
1240
1272
|
|
|
@@ -1248,6 +1280,7 @@ module ClaudeAgentSDK
|
|
|
1248
1280
|
# @return [Hash] MCP status information, including mcpServers list
|
|
1249
1281
|
def get_mcp_status
|
|
1250
1282
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1283
|
+
|
|
1251
1284
|
@query_handler.get_mcp_status
|
|
1252
1285
|
end
|
|
1253
1286
|
|
|
@@ -1261,6 +1294,7 @@ module ClaudeAgentSDK
|
|
|
1261
1294
|
# @return [Hash] Server info
|
|
1262
1295
|
def get_server_info
|
|
1263
1296
|
raise CLIConnectionError, 'Not connected. Call connect() first' unless @connected
|
|
1297
|
+
|
|
1264
1298
|
server_info
|
|
1265
1299
|
end
|
|
1266
1300
|
|
|
@@ -1343,7 +1377,7 @@ module ClaudeAgentSDK
|
|
|
1343
1377
|
end
|
|
1344
1378
|
|
|
1345
1379
|
# The connect body, wrapped by #connect so a failure triggers cleanup.
|
|
1346
|
-
def connect_inner(configured_options, prompt)
|
|
1380
|
+
def connect_inner(configured_options, prompt) # rubocop:disable Metrics/MethodLength -- connect sequence kept in order; #connect wraps it for cleanup
|
|
1347
1381
|
# Client always uses streaming mode; keep stdin open for bidirectional
|
|
1348
1382
|
# communication. Observers were already resolved by #connect.
|
|
1349
1383
|
@transport = @transport_class.new(configured_options, **@transport_args)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.37.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ya-luotao
|
|
@@ -162,6 +162,16 @@ files:
|
|
|
162
162
|
- lib/claude_agent_sdk/transcript_mirror_batcher.rb
|
|
163
163
|
- lib/claude_agent_sdk/transport.rb
|
|
164
164
|
- lib/claude_agent_sdk/types.rb
|
|
165
|
+
- lib/claude_agent_sdk/types/attributes.rb
|
|
166
|
+
- lib/claude_agent_sdk/types/base.rb
|
|
167
|
+
- lib/claude_agent_sdk/types/content_blocks.rb
|
|
168
|
+
- lib/claude_agent_sdk/types/hooks.rb
|
|
169
|
+
- lib/claude_agent_sdk/types/mcp.rb
|
|
170
|
+
- lib/claude_agent_sdk/types/messages.rb
|
|
171
|
+
- lib/claude_agent_sdk/types/option_values.rb
|
|
172
|
+
- lib/claude_agent_sdk/types/options.rb
|
|
173
|
+
- lib/claude_agent_sdk/types/permissions.rb
|
|
174
|
+
- lib/claude_agent_sdk/types/sessions.rb
|
|
165
175
|
- lib/claude_agent_sdk/version.rb
|
|
166
176
|
- lib/generators/claude_agent_sdk/install/install_generator.rb
|
|
167
177
|
- lib/generators/claude_agent_sdk/install/templates/claude_agent_sdk.rb.tt
|