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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +7 -3
- data/UPGRADING-1.0.md +151 -0
- data/docs/client.md +26 -1
- data/docs/errors.md +6 -0
- data/docs/hooks-and-permissions.md +5 -3
- data/docs/mcp-servers.md +1 -2
- data/docs/sessions.md +101 -3
- data/docs/types.md +109 -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 +1 -1
- data/lib/claude_agent_sdk/errors.rb +10 -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 +31 -16
- 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 +236 -0
- data/lib/claude_agent_sdk/types/base.rb +322 -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 +62 -28
- data/sig/claude_agent_sdk/cancellation_signal.rbs +14 -0
- data/sig/claude_agent_sdk/configuration.rbs +14 -0
- data/sig/claude_agent_sdk/errors.rbs +86 -0
- data/sig/claude_agent_sdk/observer.rbs +42 -0
- data/sig/claude_agent_sdk/railtie.rbs +10 -0
- data/sig/claude_agent_sdk/sdk_mcp_server.rbs +76 -0
- data/sig/claude_agent_sdk/session_store.rbs +105 -0
- data/sig/claude_agent_sdk/streaming.rbs +15 -0
- data/sig/claude_agent_sdk/transport.rbs +98 -0
- data/sig/claude_agent_sdk/types/base.rbs +39 -0
- data/sig/claude_agent_sdk/types/content_blocks.rbs +79 -0
- data/sig/claude_agent_sdk/types/hooks.rbs +528 -0
- data/sig/claude_agent_sdk/types/mcp.rbs +216 -0
- data/sig/claude_agent_sdk/types/messages.rbs +586 -0
- data/sig/claude_agent_sdk/types/option_values.rbs +245 -0
- data/sig/claude_agent_sdk/types/options.rbs +288 -0
- data/sig/claude_agent_sdk/types/permissions.rbs +108 -0
- data/sig/claude_agent_sdk/types/sessions.rbs +66 -0
- data/sig/claude_agent_sdk.rbs +231 -0
- data/sig/manifest.yaml +5 -0
- metadata +32 -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.
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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)
|
|
@@ -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
|