claude-agent-sdk 0.36.0 → 1.0.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.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +7 -3
  4. data/UPGRADING-1.0.md +151 -0
  5. data/docs/client.md +26 -1
  6. data/docs/errors.md +6 -0
  7. data/docs/hooks-and-permissions.md +5 -3
  8. data/docs/mcp-servers.md +1 -2
  9. data/docs/sessions.md +101 -3
  10. data/docs/types.md +109 -4
  11. data/lib/claude_agent_sdk/cli_installer.rb +30 -3
  12. data/lib/claude_agent_sdk/command_builder.rb +109 -98
  13. data/lib/claude_agent_sdk/deprecation.rb +1 -1
  14. data/lib/claude_agent_sdk/errors.rb +10 -0
  15. data/lib/claude_agent_sdk/fiber_boundary.rb +2 -0
  16. data/lib/claude_agent_sdk/instrumentation/otel.rb +15 -7
  17. data/lib/claude_agent_sdk/message_parser.rb +23 -9
  18. data/lib/claude_agent_sdk/observer.rb +2 -1
  19. data/lib/claude_agent_sdk/option_warnings.rb +2 -0
  20. data/lib/claude_agent_sdk/query.rb +50 -43
  21. data/lib/claude_agent_sdk/sdk_mcp_server.rb +22 -13
  22. data/lib/claude_agent_sdk/session_mutations.rb +20 -8
  23. data/lib/claude_agent_sdk/session_resume.rb +31 -16
  24. data/lib/claude_agent_sdk/session_store.rb +7 -3
  25. data/lib/claude_agent_sdk/session_summary.rb +4 -2
  26. data/lib/claude_agent_sdk/sessions.rb +8 -6
  27. data/lib/claude_agent_sdk/streaming.rb +1 -1
  28. data/lib/claude_agent_sdk/subprocess_cli_transport.rb +72 -40
  29. data/lib/claude_agent_sdk/testing/session_store_conformance.rb +14 -10
  30. data/lib/claude_agent_sdk/transcript_mirror_batcher.rb +2 -0
  31. data/lib/claude_agent_sdk/types/attributes.rb +236 -0
  32. data/lib/claude_agent_sdk/types/base.rb +322 -0
  33. data/lib/claude_agent_sdk/types/content_blocks.rb +57 -0
  34. data/lib/claude_agent_sdk/types/hooks.rb +640 -0
  35. data/lib/claude_agent_sdk/types/mcp.rb +232 -0
  36. data/lib/claude_agent_sdk/types/messages.rb +614 -0
  37. data/lib/claude_agent_sdk/types/option_values.rb +302 -0
  38. data/lib/claude_agent_sdk/types/options.rb +352 -0
  39. data/lib/claude_agent_sdk/types/permissions.rb +107 -0
  40. data/lib/claude_agent_sdk/types/sessions.rb +10 -0
  41. data/lib/claude_agent_sdk/types.rb +13 -2534
  42. data/lib/claude_agent_sdk/version.rb +1 -1
  43. data/lib/claude_agent_sdk.rb +62 -28
  44. data/sig/claude_agent_sdk/cancellation_signal.rbs +14 -0
  45. data/sig/claude_agent_sdk/configuration.rbs +14 -0
  46. data/sig/claude_agent_sdk/errors.rbs +86 -0
  47. data/sig/claude_agent_sdk/observer.rbs +42 -0
  48. data/sig/claude_agent_sdk/railtie.rbs +10 -0
  49. data/sig/claude_agent_sdk/sdk_mcp_server.rbs +76 -0
  50. data/sig/claude_agent_sdk/session_store.rbs +105 -0
  51. data/sig/claude_agent_sdk/streaming.rbs +15 -0
  52. data/sig/claude_agent_sdk/transport.rbs +98 -0
  53. data/sig/claude_agent_sdk/types/base.rbs +39 -0
  54. data/sig/claude_agent_sdk/types/content_blocks.rbs +79 -0
  55. data/sig/claude_agent_sdk/types/hooks.rbs +528 -0
  56. data/sig/claude_agent_sdk/types/mcp.rbs +216 -0
  57. data/sig/claude_agent_sdk/types/messages.rbs +586 -0
  58. data/sig/claude_agent_sdk/types/option_values.rbs +245 -0
  59. data/sig/claude_agent_sdk/types/options.rbs +288 -0
  60. data/sig/claude_agent_sdk/types/permissions.rbs +108 -0
  61. data/sig/claude_agent_sdk/types/sessions.rbs +66 -0
  62. data/sig/claude_agent_sdk.rbs +231 -0
  63. data/sig/manifest.yaml +5 -0
  64. metadata +32 -1
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '0.36.0'
4
+ VERSION = '1.0.0'
5
5
  end
@@ -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
- raise ArgumentError, 'can_use_tool callback cannot be used with permission_prompt_tool_name' if options.permission_prompt_tool_name
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.
@@ -537,27 +541,27 @@ module ClaudeAgentSDK
537
541
  SessionSummary.fold_session_summary(prev, key, entries)
538
542
  end
539
543
 
540
- # ---- Deprecated store twins (removed in 1.0) ----
544
+ # ---- Deprecated store twins (removed in 2.0) ----
541
545
  #
542
546
  # Each forwards to the same implementation as before — not to the merged
543
547
  # function, so a nil session_store keeps failing as it always did instead
544
548
  # of silently reading local disk — after one warning per method per process.
545
549
 
546
- # @deprecated Use {.list_sessions} with +session_store:+. Removed in 1.0.
550
+ # @deprecated Use {.list_sessions} with +session_store:+. Removed in 2.0.
547
551
  # @return [Array<SDKSessionInfo>] sorted by last_modified descending
548
552
  def self.list_sessions_from_store(session_store:, directory: nil, limit: nil, offset: 0)
549
553
  Deprecation.warn_once(:list_sessions_from_store, 'list_sessions(session_store: store)')
550
554
  Sessions.list_sessions_from_store(session_store: session_store, directory: directory, limit: limit, offset: offset)
551
555
  end
552
556
 
553
- # @deprecated Use {.get_session_info} with +session_store:+. Removed in 1.0.
557
+ # @deprecated Use {.get_session_info} with +session_store:+. Removed in 2.0.
554
558
  # @return [SDKSessionInfo, nil]
555
559
  def self.get_session_info_from_store(session_store:, session_id:, directory: nil)
556
560
  Deprecation.warn_once(:get_session_info_from_store, 'get_session_info(session_store: store, ...)')
557
561
  Sessions.get_session_info_from_store(session_store: session_store, session_id: session_id, directory: directory)
558
562
  end
559
563
 
560
- # @deprecated Use {.get_session_messages} with +session_store:+. Removed in 1.0.
564
+ # @deprecated Use {.get_session_messages} with +session_store:+. Removed in 2.0.
561
565
  # @return [Array<SessionMessage>]
562
566
  def self.get_session_messages_from_store(session_store:, session_id:, directory: nil, limit: nil, offset: 0)
563
567
  Deprecation.warn_once(:get_session_messages_from_store, 'get_session_messages(session_store: store, ...)')
@@ -565,14 +569,14 @@ module ClaudeAgentSDK
565
569
  directory: directory, limit: limit, offset: offset)
566
570
  end
567
571
 
568
- # @deprecated Use {.list_subagents} with +session_store:+. Removed in 1.0.
572
+ # @deprecated Use {.list_subagents} with +session_store:+. Removed in 2.0.
569
573
  # @return [Array<String>]
570
574
  def self.list_subagents_from_store(session_store:, session_id:, directory: nil)
571
575
  Deprecation.warn_once(:list_subagents_from_store, 'list_subagents(session_store: store, ...)')
572
576
  Sessions.list_subagents_from_store(session_store: session_store, session_id: session_id, directory: directory)
573
577
  end
574
578
 
575
- # @deprecated Use {.get_subagent_metadata} with +session_store:+. Removed in 1.0.
579
+ # @deprecated Use {.get_subagent_metadata} with +session_store:+. Removed in 2.0.
576
580
  # @return [Hash{String => Object}, nil]
577
581
  def self.get_subagent_metadata_from_store(session_store:, session_id:, agent_id:, directory: nil)
578
582
  Deprecation.warn_once(:get_subagent_metadata_from_store, 'get_subagent_metadata(session_store: store, ...)')
@@ -580,7 +584,7 @@ module ClaudeAgentSDK
580
584
  agent_id: agent_id, directory: directory)
581
585
  end
582
586
 
583
- # @deprecated Use {.get_subagent_messages} with +session_store:+. Removed in 1.0.
587
+ # @deprecated Use {.get_subagent_messages} with +session_store:+. Removed in 2.0.
584
588
  # @return [Array<SessionMessage>]
585
589
  def self.get_subagent_messages_from_store(session_store:, session_id:, agent_id:, directory: nil, limit: nil,
586
590
  offset: 0)
@@ -589,28 +593,28 @@ module ClaudeAgentSDK
589
593
  agent_id: agent_id, directory: directory, limit: limit, offset: offset)
590
594
  end
591
595
 
592
- # @deprecated Use {.rename_session} with +session_store:+. Removed in 1.0.
596
+ # @deprecated Use {.rename_session} with +session_store:+. Removed in 2.0.
593
597
  def self.rename_session_via_store(session_store:, session_id:, title:, directory: nil)
594
598
  Deprecation.warn_once(:rename_session_via_store, 'rename_session(session_store: store, ...)')
595
599
  SessionMutations.rename_session_via_store(session_store: session_store, session_id: session_id,
596
600
  title: title, directory: directory)
597
601
  end
598
602
 
599
- # @deprecated Use {.tag_session} with +session_store:+. Removed in 1.0.
603
+ # @deprecated Use {.tag_session} with +session_store:+. Removed in 2.0.
600
604
  def self.tag_session_via_store(session_store:, session_id:, tag:, directory: nil)
601
605
  Deprecation.warn_once(:tag_session_via_store, 'tag_session(session_store: store, ...)')
602
606
  SessionMutations.tag_session_via_store(session_store: session_store, session_id: session_id,
603
607
  tag: tag, directory: directory)
604
608
  end
605
609
 
606
- # @deprecated Use {.delete_session} with +session_store:+. Removed in 1.0.
610
+ # @deprecated Use {.delete_session} with +session_store:+. Removed in 2.0.
607
611
  def self.delete_session_via_store(session_store:, session_id:, directory: nil)
608
612
  Deprecation.warn_once(:delete_session_via_store, 'delete_session(session_store: store, ...)')
609
613
  SessionMutations.delete_session_via_store(session_store: session_store, session_id: session_id,
610
614
  directory: directory)
611
615
  end
612
616
 
613
- # @deprecated Use {.fork_session} with +session_store:+. Removed in 1.0.
617
+ # @deprecated Use {.fork_session} with +session_store:+. Removed in 2.0.
614
618
  # @return [ForkSessionResult]
615
619
  def self.fork_session_via_store(session_store:, session_id:, directory: nil, up_to_message_id: nil, title: nil)
616
620
  Deprecation.warn_once(:fork_session_via_store, 'fork_session(session_store: store, ...)')
@@ -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
- raise ArgumentError, 'prompt must be a String or an Enumerable of message Hashes/JSONL Strings (got Hash)' if prompt.is_a?(Hash)
673
- raise ArgumentError, "prompt must be a String or respond to #each (got #{prompt.class})" unless prompt.is_a?(String) || prompt.respond_to?(:each)
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
- raise ArgumentError, 'transport must respond to #connect (see ClaudeAgentSDK::Transport)' if transport && !transport.respond_to?(:connect)
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
- configured_options = SessionResume.apply_materialized_options(configured_options, materialized) if materialized
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) + "\n")
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(prompt, resolved_observers,
784
- scheduling: callback_scheduling, wrapper: callback_wrapper)
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
- raise ArgumentError, 'prompt must be a String or an Enumerable of message Hashes/JSONL Strings (got Hash)' if prompt.is_a?(Hash)
987
- raise ArgumentError, "prompt must be a String, an Enumerator, or nil (got #{prompt.class})" unless prompt.nil? || prompt.is_a?(String) || prompt.respond_to?(:each)
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
- raise ArgumentError, 'prompt must be a String or an Enumerable of message Hashes/JSONL Strings (got Hash)' if prompt.is_a?(Hash)
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&.instance_variable_get(:@initialization_result)
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)
@@ -0,0 +1,14 @@
1
+ module ClaudeAgentSDK
2
+ # Delivered as ToolPermissionContext#signal and HookContext#signal. It is
3
+ # cancelled when the CLI cancels the request (or the session closes), so a
4
+ # long-running callback can stop early.
5
+ class CancellationSignal
6
+ def initialize: () -> void
7
+
8
+ def cancelled?: () -> bool
9
+
10
+ # Blocks until cancelled or until timeout seconds pass; returns
11
+ # #cancelled?.
12
+ def wait: (?timeout: (Integer | Float)?) -> bool
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module ClaudeAgentSDK
2
+ # Global defaults set via ClaudeAgentSDK.configure, merged under every
3
+ # ClaudeAgentOptions (per-call options win).
4
+ class Configuration
5
+ # A frozen snapshot of the configured defaults ({} when unset). Keys are
6
+ # ClaudeAgentOptions option names.
7
+ attr_reader default_options: Hash[Symbol | String, untyped]
8
+
9
+ def initialize: () -> void
10
+
11
+ # Stores a deep-frozen copy; nil resets to {}.
12
+ def default_options=: (Hash[Symbol | String, untyped]? value) -> Hash[Symbol | String, untyped]?
13
+ end
14
+ end
@@ -0,0 +1,86 @@
1
+ module ClaudeAgentSDK
2
+ # Base exception for all Claude SDK errors.
3
+ class ClaudeSDKError < StandardError
4
+ end
5
+
6
+ # Raised when unable to connect to Claude Code (also: "Not connected").
7
+ class CLIConnectionError < ClaudeSDKError
8
+ end
9
+
10
+ # Raised when the control protocol does not respond in time.
11
+ class ControlRequestTimeoutError < CLIConnectionError
12
+ end
13
+
14
+ # Raised when Claude Code is not found or not installed.
15
+ class CLINotFoundError < CLIConnectionError
16
+ def initialize: (?String message, ?cli_path: (String | Pathname)?) -> void
17
+ end
18
+
19
+ # Raised when CLIInstaller cannot install the CLI binary.
20
+ class CLIInstallError < ClaudeSDKError
21
+ end
22
+
23
+ # Raised by the local-disk session APIs when the Claude config directory
24
+ # cannot be located.
25
+ class ConfigDirError < ClaudeSDKError
26
+ end
27
+
28
+ # Raised by Client#connect, ClaudeAgentSDK.query and .ask when resuming
29
+ # from ClaudeAgentOptions#session_store fails (a store call raised or
30
+ # exceeded load_timeout_ms). #cause holds the adapter's own exception.
31
+ class SessionStoreError < ClaudeSDKError
32
+ end
33
+
34
+ # Raised when the CLI process fails.
35
+ class ProcessError < ClaudeSDKError
36
+ attr_reader exit_code: Integer?
37
+
38
+ attr_reader stderr: String?
39
+
40
+ def initialize: (String message, ?exit_code: Integer?, ?stderr: String?) -> void
41
+ end
42
+
43
+ # Raised when the CLI exits non-zero after an error result. Every
44
+ # structured field is type-narrowed (nil unless the payload held the
45
+ # documented type); #data holds the payload as the CLI sent it.
46
+ class ResultError < ProcessError
47
+ attr_reader subtype: String?
48
+
49
+ attr_reader errors: Array[String]
50
+
51
+ attr_reader result: String?
52
+
53
+ attr_reader api_error_status: Integer?
54
+
55
+ attr_reader terminal_reason: String?
56
+
57
+ attr_reader session_id: String?
58
+
59
+ attr_reader data: Hash[Symbol | String, untyped]
60
+
61
+ attr_reader original_error: ProcessError?
62
+
63
+ # The exception text a `result` payload produces (any payload is
64
+ # accepted; a non-Hash reads as empty).
65
+ def self.error_text: (untyped data) -> String
66
+
67
+ def initialize: (String message, ?data: untyped, ?exit_code: Integer?, ?stderr: String?, ?original_error: ProcessError?) -> void
68
+ end
69
+
70
+ # Raised when a stdout line from the CLI is not valid JSON.
71
+ class CLIJSONDecodeError < ClaudeSDKError
72
+ attr_reader line: String
73
+
74
+ attr_reader original_error: Exception
75
+
76
+ def initialize: (String line, Exception original_error) -> void
77
+ end
78
+
79
+ # Raised when a CLI message cannot be parsed into a typed message.
80
+ class MessageParseError < ClaudeSDKError
81
+ # The offending payload, as received.
82
+ attr_reader data: untyped
83
+
84
+ def initialize: (String message, ?data: untyped) -> void
85
+ end
86
+ end
@@ -0,0 +1,42 @@
1
+ module ClaudeAgentSDK
2
+ # Base module for message observers (ClaudeAgentOptions#observers): include
3
+ # it and override the callbacks you need; each defaults to a no-op.
4
+ # Observer errors are rescued and never reach the pipeline.
5
+ module Observer
6
+ def on_user_prompt: (String prompt) -> void
7
+
8
+ def on_message: (message message) -> void
9
+
10
+ def on_error: (StandardError error) -> void
11
+
12
+ def on_close: () -> void
13
+ end
14
+
15
+ # Loaded by `require 'claude_agent_sdk/instrumentation'` (needs the
16
+ # opentelemetry-api gem).
17
+ module Instrumentation
18
+ # Emits OpenTelemetry spans (gen_ai.* + OpenInference conventions) for a
19
+ # session.
20
+ class OTelObserver
21
+ include Observer
22
+
23
+ TRACER_NAME: String
24
+
25
+ MAX_ATTRIBUTE_LENGTH: Integer
26
+
27
+ # default_attributes are added to every span. Attribute names usually
28
+ # contain dots, so they are commonly given as String keys
29
+ # (`new('user.id' => 'u1')`).
30
+ def initialize: (?tracer_name: String, **untyped default_attributes) -> void
31
+ | (Hash[String | Symbol, untyped] default_attributes) -> void
32
+
33
+ def on_user_prompt: (String prompt) -> void
34
+
35
+ def on_message: (message message) -> void
36
+
37
+ def on_error: (StandardError error) -> void
38
+
39
+ def on_close: () -> void
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ module ClaudeAgentSDK
2
+ # Rails integration (lib/claude_agent_sdk/railtie.rb), loaded only when
3
+ # Rails::Railtie is defined. Declared without its Rails::Railtie superclass
4
+ # so these signatures validate without Rails' own.
5
+ class Railtie
6
+ # A callback_wrapper that runs SDK callbacks in the Rails executor,
7
+ # deadlock-free under development code reloading.
8
+ def self.callback_wrapper: () -> _CallbackWrapper
9
+ end
10
+ end
@@ -0,0 +1,76 @@
1
+ module ClaudeAgentSDK
2
+ # What an SDK MCP tool handler returns: a String (sent as one text block),
3
+ # or a Hash with a :content Array of MCP content blocks plus optional
4
+ # :is_error / :structured_content.
5
+ type tool_result = String | Hash[Symbol | String, untyped]
6
+
7
+ # An SDK MCP tool handler (the block given to ClaudeAgentSDK.create_tool).
8
+ # args is the tools/call arguments Hash (wire_hash).
9
+ interface _ToolHandler
10
+ def call: (wire_hash args) -> tool_result
11
+ end
12
+
13
+ # An SDK MCP resource reader: returns a Hash with a :contents Array.
14
+ interface _ResourceReader
15
+ def call: () -> Hash[Symbol | String, untyped]
16
+ end
17
+
18
+ # An SDK MCP prompt generator: returns a Hash with a :messages Array.
19
+ interface _PromptGenerator
20
+ def call: (Hash[Symbol | String, untyped] args) -> Hash[Symbol | String, untyped]
21
+ end
22
+
23
+ # An in-process MCP server (built by ClaudeAgentSDK.create_sdk_mcp_server).
24
+ # The list_*/call_*/read_*/get_* methods invoke it directly, outside a
25
+ # session.
26
+ class SdkMcpServer
27
+ attr_reader name: String
28
+
29
+ attr_reader version: String
30
+
31
+ attr_reader tools: Array[SdkMcpTool]
32
+
33
+ attr_reader resources: Array[SdkMcpResource]
34
+
35
+ attr_reader prompts: Array[SdkMcpPrompt]
36
+
37
+ # The underlying server object from the `mcp` gem (MCP::Server); not
38
+ # typed here so the signatures don't depend on that gem's.
39
+ attr_reader mcp_server: untyped
40
+
41
+ # Where handlers run on DIRECT invocations (a session dispatch uses the
42
+ # session's own mode). Defaults to :thread.
43
+ attr_reader callback_scheduling: :thread | :inline
44
+
45
+ # Wrapper for DIRECT invocations (a session dispatch uses the session's).
46
+ attr_reader callback_wrapper: _CallbackWrapper?
47
+
48
+ # A String is coerced to a Symbol; anything but :thread / :inline raises
49
+ # ArgumentError.
50
+ def callback_scheduling=: (:thread | :inline | "thread" | "inline" value) -> (:thread | :inline | "thread" | "inline")
51
+
52
+ # Raises ArgumentError unless value is callable or nil.
53
+ def callback_wrapper=: (_CallbackWrapper? value) -> _CallbackWrapper?
54
+
55
+ def initialize: (name: String, ?version: String, ?tools: Array[SdkMcpTool], ?resources: Array[SdkMcpResource], ?prompts: Array[SdkMcpPrompt]) -> void
56
+
57
+ # Tool definitions: { name:, description:, inputSchema:, annotations:?, _meta:? }.
58
+ def list_tools: () -> Array[Hash[Symbol, untyped]]
59
+
60
+ # Runs the tool's handler. Failures come back in-band with isError: true
61
+ # (a String handler result is wrapped into a text block).
62
+ def call_tool: (String name, Hash[Symbol | String, untyped] arguments) -> Hash[Symbol | String, untyped]
63
+
64
+ def list_resources: () -> Array[Hash[Symbol, untyped]]
65
+
66
+ # Raises when the resource does not exist or its reader returns no
67
+ # :contents.
68
+ def read_resource: (String uri) -> Hash[Symbol | String, untyped]
69
+
70
+ def list_prompts: () -> Array[Hash[Symbol, untyped]]
71
+
72
+ # Raises when the prompt does not exist or its generator returns no
73
+ # :messages.
74
+ def get_prompt: (String name, ?Hash[Symbol | String, untyped] arguments) -> Hash[Symbol | String, untyped]
75
+ end
76
+ end