claude-agent-sdk 1.1.0 → 1.2.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/.yardopts +10 -0
- data/CHANGELOG.md +90 -0
- data/README.md +43 -31
- data/docs/cli-installer.md +26 -4
- data/docs/client.md +29 -11
- data/docs/configuration.md +164 -1
- data/docs/errors.md +32 -2
- data/docs/hooks-and-permissions.md +30 -10
- data/docs/mcp-servers.md +30 -9
- data/docs/observability.md +61 -10
- data/docs/options.md +232 -0
- data/docs/rails.md +263 -18
- data/docs/sessions.md +40 -12
- data/docs/subagents.md +1 -1
- data/docs/types.md +100 -11
- data/lib/claude_agent_sdk/cli_installer.rb +140 -19
- data/lib/claude_agent_sdk/command_builder.rb +84 -27
- data/lib/claude_agent_sdk/fiber_boundary.rb +45 -2
- data/lib/claude_agent_sdk/instrumentation/otel.rb +90 -28
- data/lib/claude_agent_sdk/query.rb +228 -77
- data/lib/claude_agent_sdk/railtie.rb +27 -2
- data/lib/claude_agent_sdk/sdk_mcp_server.rb +78 -26
- data/lib/claude_agent_sdk/session_mutations.rb +112 -92
- data/lib/claude_agent_sdk/session_resume.rb +356 -39
- data/lib/claude_agent_sdk/session_store.rb +31 -2
- data/lib/claude_agent_sdk/sessions.rb +720 -138
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +227 -29
- data/lib/claude_agent_sdk/testing/session_store_conformance.rb +18 -7
- data/lib/claude_agent_sdk/transcript_mirror_batcher.rb +45 -37
- data/lib/claude_agent_sdk/transport.rb +28 -12
- data/lib/claude_agent_sdk/types/attributes.rb +9 -0
- data/lib/claude_agent_sdk/types/base.rb +85 -15
- data/lib/claude_agent_sdk/types/hooks.rb +73 -0
- data/lib/claude_agent_sdk/types/mcp.rb +37 -1
- data/lib/claude_agent_sdk/types/option_values.rb +186 -4
- data/lib/claude_agent_sdk/types/options.rb +35 -5
- data/lib/claude_agent_sdk/types/permissions.rb +18 -9
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +94 -46
- data/lib/generators/claude_agent_sdk/install/templates/claude_agent_sdk.rb.tt +6 -0
- data/sig/claude_agent_sdk/types/hooks.rbs +6 -3
- data/sig/claude_agent_sdk/types/option_values.rbs +23 -6
- data/sig/claude_agent_sdk/types/options.rbs +11 -7
- data/sig/claude_agent_sdk/types/permissions.rbs +4 -2
- metadata +6 -4
|
@@ -18,8 +18,10 @@ module ClaudeAgentSDK
|
|
|
18
18
|
|
|
19
19
|
# Claude Agent Options for configuring queries
|
|
20
20
|
class ClaudeAgentOptions < Type
|
|
21
|
-
# `env` routinely carries credentials (ANTHROPIC_API_KEY, ...).
|
|
22
|
-
|
|
21
|
+
# `env` routinely carries credentials (ANTHROPIC_API_KEY, ...). So does
|
|
22
|
+
# `settings` (its own `env`, `apiKeyHelper`), as a Hash or a JSON String,
|
|
23
|
+
# and an `extra_args` value can be one.
|
|
24
|
+
inspect_filtered :env, :settings, :extra_args
|
|
23
25
|
|
|
24
26
|
attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
|
|
25
27
|
:resume, :resume_session_at, :session_id, :max_turns, :disallowed_tools,
|
|
@@ -345,16 +347,44 @@ module ClaudeAgentSDK
|
|
|
345
347
|
self.callback_scheduling = :thread if callback_scheduling.nil?
|
|
346
348
|
end
|
|
347
349
|
|
|
350
|
+
# `mcp_servers` holds typed configs, which filter themselves, next to raw
|
|
351
|
+
# Hash configs carrying the same credentials: those are rendered by
|
|
352
|
+
# Type#inspect_mcp_server_config. Anything but a Hash (the JSON of a
|
|
353
|
+
# config as a String, or the path to one) is replaced outright, like a
|
|
354
|
+
# String `settings`.
|
|
355
|
+
def inspect_attributes
|
|
356
|
+
super.map { |name, value| [name, name == 'mcp_servers' ? inspect_mcp_servers(value) : value] }
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def inspect_mcp_servers(servers)
|
|
360
|
+
return '[FILTERED]' unless servers.is_a?(Hash)
|
|
361
|
+
|
|
362
|
+
servers.to_h { |name, config| [name, config.is_a?(Hash) ? inspect_mcp_server_config(config) : config] }
|
|
363
|
+
rescue StandardError
|
|
364
|
+
'[FILTERED]'
|
|
365
|
+
end
|
|
366
|
+
|
|
348
367
|
# Strict key validation: unlike other Type subclasses (which silently drop
|
|
349
368
|
# unknown keys for forward-compat with newer CLI output), ClaudeAgentOptions
|
|
350
369
|
# is a developer-facing config object — typos should fail loudly.
|
|
351
370
|
def assign_attribute(name, value)
|
|
352
|
-
setter =
|
|
353
|
-
raise ArgumentError, "unknown ClaudeAgentOptions option: #{name.inspect}" unless
|
|
371
|
+
setter = option_setter(normalize_name(name))
|
|
372
|
+
raise ArgumentError, "unknown ClaudeAgentOptions option: #{name.inspect}" unless setter
|
|
354
373
|
|
|
355
374
|
public_send(setter, value)
|
|
356
375
|
end
|
|
357
376
|
|
|
377
|
+
# The writer of the option with this normalized name, nil when the name
|
|
378
|
+
# is not an option. An option is a declared attribute, or a setter user
|
|
379
|
+
# code defined (on a subclass, a module it includes, or the object);
|
|
380
|
+
# respond_to? alone would also answer for '[]' (#[]=) and '=' (#==).
|
|
381
|
+
def option_setter(normalized)
|
|
382
|
+
setter = :"#{normalized}="
|
|
383
|
+
return unless respond_to?(setter)
|
|
384
|
+
|
|
385
|
+
setter if self.class.attribute?(normalized) || user_defined_method?(setter)
|
|
386
|
+
end
|
|
387
|
+
|
|
358
388
|
# Merge caller-provided attributes with configured defaults.
|
|
359
389
|
# Only keys the caller explicitly passed are treated as overrides;
|
|
360
390
|
# method-signature defaults ([], {}, false) are NOT present unless the caller wrote them.
|
|
@@ -403,7 +433,7 @@ module ClaudeAgentSDK
|
|
|
403
433
|
# reports the typo exactly as the developer wrote it.
|
|
404
434
|
def option_key(name)
|
|
405
435
|
normalized = normalize_name(name)
|
|
406
|
-
|
|
436
|
+
option_setter(normalized) ? normalized.to_sym : name
|
|
407
437
|
end
|
|
408
438
|
end
|
|
409
439
|
end
|
|
@@ -34,20 +34,21 @@ module ClaudeAgentSDK
|
|
|
34
34
|
@rules = value&.map { |rule| rule.is_a?(Hash) ? PermissionRuleValue.new(rule) : rule }
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# The wire form. The CLI validates the updatedPermissions of a
|
|
38
|
+
# can_use_tool reply as one unit and drops the whole array when a single
|
|
39
|
+
# entry does not fit its schema, so two things it rejects are never
|
|
40
|
+
# written: a rule without content has no ruleContent key (the schema
|
|
41
|
+
# wants a String or no key, not null), and an update that has a type but
|
|
42
|
+
# no destination goes to 'session' — the narrowest one: it lasts for this
|
|
43
|
+
# run and writes no settings file (the default Python PR #1330 proposes).
|
|
37
44
|
def to_h
|
|
38
45
|
result = { type: @type }
|
|
39
|
-
|
|
46
|
+
destination = @destination || ('session' unless @type.nil?)
|
|
47
|
+
result[:destination] = destination if destination
|
|
40
48
|
|
|
41
49
|
case @type
|
|
42
50
|
when 'addRules', 'replaceRules', 'removeRules'
|
|
43
|
-
if @rules
|
|
44
|
-
result[:rules] = @rules.map do |rule|
|
|
45
|
-
{
|
|
46
|
-
toolName: rule.tool_name,
|
|
47
|
-
ruleContent: rule.rule_content
|
|
48
|
-
}
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
+
result[:rules] = @rules.map { |rule| rule_to_h(rule) } if @rules
|
|
51
52
|
result[:behavior] = @behavior if @behavior
|
|
52
53
|
when 'setMode'
|
|
53
54
|
result[:mode] = @mode if @mode
|
|
@@ -57,6 +58,14 @@ module ClaudeAgentSDK
|
|
|
57
58
|
|
|
58
59
|
result
|
|
59
60
|
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def rule_to_h(rule)
|
|
65
|
+
wire_rule = { toolName: rule.tool_name }
|
|
66
|
+
wire_rule[:ruleContent] = rule.rule_content unless rule.rule_content.nil?
|
|
67
|
+
wire_rule
|
|
68
|
+
end
|
|
60
69
|
end
|
|
61
70
|
|
|
62
71
|
# Tool permission context delivered to `can_use_tool` callbacks.
|
data/lib/claude_agent_sdk.rb
CHANGED
|
@@ -79,7 +79,10 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
# Internal: normalize hook lists for the control protocol. An absent or
|
|
82
|
-
# disabled event must not become an empty registration in initialize.
|
|
82
|
+
# disabled event must not become an empty registration in initialize. An
|
|
83
|
+
# event written as a String under one key and as a Symbol under another
|
|
84
|
+
# ('PreToolUse' and :PreToolUse) is one event: its matcher lists are
|
|
85
|
+
# joined in the order they were written, never replaced.
|
|
83
86
|
# @api private
|
|
84
87
|
def self.convert_hooks_to_internal_format(hooks)
|
|
85
88
|
return nil unless hooks
|
|
@@ -94,7 +97,7 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
94
97
|
config[:timeout] = matcher.timeout if matcher.timeout
|
|
95
98
|
entries << config
|
|
96
99
|
end
|
|
97
|
-
internal_hooks[event.to_s]
|
|
100
|
+
(internal_hooks[event.to_s] ||= []).concat(entries) unless entries.empty?
|
|
98
101
|
end
|
|
99
102
|
internal_hooks.empty? ? nil : internal_hooks
|
|
100
103
|
end
|
|
@@ -139,7 +142,8 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
139
142
|
eds = system_prompt.exclude_dynamic_sections
|
|
140
143
|
return eds if [true, false].include?(eds)
|
|
141
144
|
elsif system_prompt.is_a?(Hash)
|
|
142
|
-
|
|
145
|
+
# The tag may be a Symbol (type: :preset), as CommandBuilder reads it.
|
|
146
|
+
type = (system_prompt[:type] || system_prompt['type']).to_s
|
|
143
147
|
if type == 'preset'
|
|
144
148
|
eds = system_prompt.fetch(:exclude_dynamic_sections) { system_prompt['exclude_dynamic_sections'] }
|
|
145
149
|
return eds if [true, false].include?(eds)
|
|
@@ -160,7 +164,7 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
160
164
|
snapshot = system_prompt.snapshot
|
|
161
165
|
return snapshot if [true, false].include?(snapshot)
|
|
162
166
|
when Hash
|
|
163
|
-
type = system_prompt[:type] || system_prompt['type']
|
|
167
|
+
type = (system_prompt[:type] || system_prompt['type']).to_s
|
|
164
168
|
if %w[preset custom].include?(type)
|
|
165
169
|
snapshot = system_prompt.fetch(:snapshot) { system_prompt['snapshot'] }
|
|
166
170
|
return snapshot if [true, false].include?(snapshot)
|
|
@@ -173,14 +177,39 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
173
177
|
# Each observer is invoked through FiberBoundary so that user code runs
|
|
174
178
|
# on a plain thread (no Fiber scheduler) even when called from inside
|
|
175
179
|
# the SDK's Async reactor — or in place when scheduling is :inline.
|
|
180
|
+
#
|
|
181
|
+
# What the observer raises, and what its callback_wrapper raises, is
|
|
182
|
+
# contained where it happens: inside the hop, on the execution context
|
|
183
|
+
# the observer runs on. The rescue must not sit around the hop, because
|
|
184
|
+
# the calling fiber WAITS there, and an exception raised into a waiting
|
|
185
|
+
# fiber is not the observer's — a caller's `task.with_timeout` /
|
|
186
|
+
# `Timeout.timeout` deadline is delivered exactly that way. Rescued out
|
|
187
|
+
# here, an expired deadline was swallowed as if the observer had failed,
|
|
188
|
+
# and the turn carried on. So the wrapper is composed inside the rescued
|
|
189
|
+
# body rather than handed to FiberBoundary.invoke, which would run it
|
|
190
|
+
# outside that rescue.
|
|
191
|
+
#
|
|
192
|
+
# Known residue with `scheduling: :inline` (and with no scheduler at all):
|
|
193
|
+
# the observer runs on the calling fiber itself, so a deadline that lands
|
|
194
|
+
# while the observer is suspended is raised inside the observer's own
|
|
195
|
+
# frames, cannot be told from the observer's own timeout, and is still
|
|
196
|
+
# swallowed here.
|
|
176
197
|
# @api private
|
|
177
198
|
def self.notify_observers(observers, method, *args, scheduling: :thread, wrapper: nil)
|
|
178
199
|
observers.each do |obs|
|
|
179
|
-
FiberBoundary.invoke(scheduling: scheduling
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
200
|
+
FiberBoundary.invoke(scheduling: scheduling) do
|
|
201
|
+
invocation = proc { obs.send(method, *args) }
|
|
202
|
+
wrapper ? wrapper.call(invocation) : invocation.call
|
|
203
|
+
rescue StandardError, ScriptError
|
|
204
|
+
# ScriptError too: NotImplementedError < ScriptError (not
|
|
205
|
+
# StandardError), and a stubbed observer must never mask the original
|
|
206
|
+
# error being notified or abort connect/teardown cleanup.
|
|
207
|
+
nil
|
|
208
|
+
end
|
|
209
|
+
rescue ScriptError
|
|
210
|
+
# A ScriptError raised around the body rather than in it (the hop's own
|
|
211
|
+
# plumbing). No deadline is a ScriptError, so nothing a caller injects
|
|
212
|
+
# into the waiting fiber is caught here.
|
|
184
213
|
nil
|
|
185
214
|
end
|
|
186
215
|
end
|
|
@@ -703,7 +732,14 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
703
732
|
raise ArgumentError, 'transport must respond to #connect (see ClaudeAgentSDK::Transport)'
|
|
704
733
|
end
|
|
705
734
|
|
|
706
|
-
|
|
735
|
+
# finished: false tells Async that a waiter handles this task's failure
|
|
736
|
+
# (the .wait at the end re-raises it), as Kernel#Sync does for its own
|
|
737
|
+
# task. Without it a task that fails before anyone waits for it — always
|
|
738
|
+
# the case outside a reactor, where Async runs the whole task before it
|
|
739
|
+
# returns — is also logged as "Task may have ended with unhandled
|
|
740
|
+
# exception", message and backtrace included, although the caller gets
|
|
741
|
+
# the same error raised and may well rescue it.
|
|
742
|
+
Async(finished: false, &FiberBoundary.capture_otel_context do # rubocop:disable Metrics/BlockLength -- the reactor task body of query()
|
|
707
743
|
materialized = nil
|
|
708
744
|
query_handler = nil
|
|
709
745
|
begin
|
|
@@ -903,16 +939,13 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
903
939
|
# The Client class always uses streaming mode for bidirectional communication.
|
|
904
940
|
#
|
|
905
941
|
# @example Basic usage
|
|
906
|
-
#
|
|
907
|
-
#
|
|
908
|
-
#
|
|
909
|
-
#
|
|
942
|
+
# # Client.open connects, yields the client and always disconnects,
|
|
943
|
+
# # also when the block raises.
|
|
944
|
+
# ClaudeAgentSDK::Client.open do |client|
|
|
910
945
|
# client.query("What is the capital of France?")
|
|
911
946
|
# client.receive_response do |msg|
|
|
912
947
|
# puts msg if msg.is_a?(ClaudeAgentSDK::AssistantMessage)
|
|
913
948
|
# end
|
|
914
|
-
#
|
|
915
|
-
# client.disconnect
|
|
916
949
|
# end
|
|
917
950
|
#
|
|
918
951
|
# @example With hooks
|
|
@@ -1043,17 +1076,22 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
1043
1076
|
# (The enumerator branch streams in the background and cannot raise
|
|
1044
1077
|
# out of connect.) No on_close follows for pre-handshake failures
|
|
1045
1078
|
# (disconnect gates it on @connected): the session never opened.
|
|
1046
|
-
notify_error(e) if e.is_a?(StandardError) && !@connected
|
|
1047
|
-
# Tear down the partial connect, but never let a cleanup failure (e.g. a
|
|
1048
|
-
# custom transport whose #close raises) mask the original connect error.
|
|
1049
|
-
# Rescue Exception (not StandardError) so reactor cancellation
|
|
1050
|
-
# (Async::Stop < Exception) after materialize_resume set @materialized
|
|
1051
|
-
# still runs disconnect -> @materialized.cleanup, never leaking the temp
|
|
1052
|
-
# CLAUDE_CONFIG_DIR that holds the redacted .credentials.json copy.
|
|
1053
1079
|
begin
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1080
|
+
notify_error(e) if e.is_a?(StandardError) && !@connected
|
|
1081
|
+
ensure
|
|
1082
|
+
# Tear down the partial connect, but never let a cleanup failure (e.g. a
|
|
1083
|
+
# custom transport whose #close raises) mask the original connect error.
|
|
1084
|
+
# Rescue Exception (not StandardError) so reactor cancellation
|
|
1085
|
+
# (Async::Stop < Exception) after materialize_resume set @materialized
|
|
1086
|
+
# still runs disconnect -> @materialized.cleanup, never leaking the temp
|
|
1087
|
+
# CLAUDE_CONFIG_DIR that holds the redacted .credentials.json copy.
|
|
1088
|
+
# In an ensure: an exception raised into this fiber while it waits
|
|
1089
|
+
# for the on_error observer (a caller's deadline) must not skip it.
|
|
1090
|
+
begin
|
|
1091
|
+
disconnect
|
|
1092
|
+
rescue StandardError => cleanup_error
|
|
1093
|
+
warn "Claude SDK: cleanup after failed connect raised: #{cleanup_error.message}"
|
|
1094
|
+
end
|
|
1057
1095
|
end
|
|
1058
1096
|
raise
|
|
1059
1097
|
end
|
|
@@ -1314,11 +1352,11 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
1314
1352
|
# holds, in every scheduling mode, for a streaming-input enumerator that
|
|
1315
1353
|
# calls disconnect: it is iterated on the reactor inside a task the close
|
|
1316
1354
|
# stops, so it unwinds with Async::Stop once the teardown has completed.
|
|
1355
|
+
#
|
|
1356
|
+
# Interrupted while an observer's on_close runs — the caller's deadline
|
|
1357
|
+
# expires, or its task is stopped — disconnect still completes the
|
|
1358
|
+
# teardown, then lets the interruption propagate.
|
|
1317
1359
|
def disconnect
|
|
1318
|
-
if @connected
|
|
1319
|
-
ClaudeAgentSDK.notify_observers(@resolved_observers || [], :on_close,
|
|
1320
|
-
scheduling: @callback_scheduling, wrapper: @callback_wrapper)
|
|
1321
|
-
end
|
|
1322
1360
|
# Tear down whatever exists — robust to a partial/failed connect, where
|
|
1323
1361
|
# @connected is still false but a transport and/or materialized temp dir
|
|
1324
1362
|
# were already created. #close on the query handler also closes the
|
|
@@ -1335,26 +1373,36 @@ module ClaudeAgentSDK # rubocop:disable Metrics/ModuleLength -- the public entry
|
|
|
1335
1373
|
# and the materialized-dir decision at the bottom needs to ask it.
|
|
1336
1374
|
query_handler = @query_handler
|
|
1337
1375
|
begin
|
|
1338
|
-
|
|
1376
|
+
# Notified inside the begin: an exception raised into this fiber while
|
|
1377
|
+
# it waits for an observer (a caller's deadline) propagates, and must
|
|
1378
|
+
# not skip the teardown below.
|
|
1379
|
+
if @connected
|
|
1380
|
+
ClaudeAgentSDK.notify_observers(@resolved_observers || [], :on_close,
|
|
1381
|
+
scheduling: @callback_scheduling, wrapper: @callback_wrapper)
|
|
1382
|
+
end
|
|
1339
1383
|
ensure
|
|
1340
|
-
@query_handler = nil
|
|
1341
1384
|
begin
|
|
1342
|
-
@
|
|
1385
|
+
@query_handler&.close
|
|
1343
1386
|
ensure
|
|
1344
|
-
@
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1387
|
+
@query_handler = nil
|
|
1388
|
+
begin
|
|
1389
|
+
@transport&.close
|
|
1390
|
+
ensure
|
|
1391
|
+
@transport = nil
|
|
1392
|
+
@connected = false
|
|
1393
|
+
# Remove the materialized resume temp dir AFTER the subprocess
|
|
1394
|
+
# exited — unless the mirror dropped batches: the store copy is then
|
|
1395
|
+
# incomplete and the temp dir holds the only copy of the dropped
|
|
1396
|
+
# turns, so it is preserved (scrubbed of credentials) with a warning
|
|
1397
|
+
# instead of deleted.
|
|
1398
|
+
if @materialized
|
|
1399
|
+
if query_handler&.mirror_batches_dropped?
|
|
1400
|
+
@materialized.preserve_transcripts
|
|
1401
|
+
else
|
|
1402
|
+
@materialized.cleanup
|
|
1403
|
+
end
|
|
1404
|
+
@materialized = nil
|
|
1356
1405
|
end
|
|
1357
|
-
@materialized = nil
|
|
1358
1406
|
end
|
|
1359
1407
|
end
|
|
1360
1408
|
end
|
|
@@ -22,6 +22,12 @@ ClaudeAgentSDK.configure do |config|
|
|
|
22
22
|
# whatever the process cwd. To run a binary from somewhere else instead:
|
|
23
23
|
# cli_path: '/usr/local/bin/claude',
|
|
24
24
|
|
|
25
|
+
# Multi-user apps: turn off the CLI's auto-memory — kept per project
|
|
26
|
+
# directory, read by every session and written by claude_code-preset
|
|
27
|
+
# sessions, so one user's "remember ..." reaches all others (the value
|
|
28
|
+
# must stay '1'; '0' forces it on). See docs/rails.md ("Per-user isolation").
|
|
29
|
+
# env: { 'CLAUDE_CODE_DISABLE_AUTO_MEMORY' => '1' },
|
|
30
|
+
|
|
25
31
|
# OpenTelemetry tracing (Langfuse, Honeycomb, ...). A factory lambda gives
|
|
26
32
|
# every query/session its own observer — safe under Puma and job threads.
|
|
27
33
|
# observers: [-> { ClaudeAgentSDK::Instrumentation::OTelObserver.new }],
|
|
@@ -47,8 +47,9 @@ module ClaudeAgentSDK
|
|
|
47
47
|
| CwdChangedHookSpecificOutput
|
|
48
48
|
| FileChangedHookSpecificOutput
|
|
49
49
|
|
|
50
|
-
# What a hook callback returns: a Hash (
|
|
51
|
-
# typed output, or nil
|
|
50
|
+
# What a hook callback returns: a Hash (Symbol or String keys, snake_case
|
|
51
|
+
# or camelCase, also inside hook_specific_output), a typed output, or nil
|
|
52
|
+
# (treated as {}).
|
|
52
53
|
type hook_output = Hash[Symbol | String, untyped] | SyncHookJSONOutput | AsyncHookJSONOutput | nil
|
|
53
54
|
|
|
54
55
|
# A hook callback (HookMatcher#hooks entries). tool_use_id is nil for
|
|
@@ -520,7 +521,9 @@ module ClaudeAgentSDK
|
|
|
520
521
|
|
|
521
522
|
attr_accessor reason: String?
|
|
522
523
|
|
|
523
|
-
# A typed output, or the equivalent Hash (camelCase
|
|
524
|
+
# A typed output, or the equivalent Hash (snake_case or camelCase keys).
|
|
525
|
+
# #to_h returns a Hash as written; the SDK puts its keys in wire spelling
|
|
526
|
+
# when it sends the output.
|
|
524
527
|
attr_accessor hook_specific_output: (hook_specific_output | Hash[Symbol | String, untyped])?
|
|
525
528
|
|
|
526
529
|
def to_h: () -> Hash[Symbol, untyped]
|
|
@@ -7,6 +7,16 @@ module ClaudeAgentSDK
|
|
|
7
7
|
# the Hash form (String or camelCase keys, e.g. `klass.new(value.to_h)`)
|
|
8
8
|
# is accepted too. An unknown key raises ArgumentError. Discriminators
|
|
9
9
|
# (`type`) are fixed by the class and read-only.
|
|
10
|
+
#
|
|
11
|
+
# The keyword overload documents the accepted keys and their types; no
|
|
12
|
+
# type checker enforces it. `klass.new(typo: 1)` is also a Symbol-keyed
|
|
13
|
+
# Hash, which the second overload accepts whatever its keys and values, so
|
|
14
|
+
# Steep reports neither a misspelled keyword nor a wrong value type, and
|
|
15
|
+
# the runtime checker behind `rake rbs:test` never reports a constructor
|
|
16
|
+
# mismatch (it only checks the attribute setters the constructor calls).
|
|
17
|
+
# The enforcement is the ArgumentError the constructor raises at runtime
|
|
18
|
+
# for an unknown key. This holds for every constructor in sig/ that has
|
|
19
|
+
# both overloads.
|
|
10
20
|
|
|
11
21
|
# Adaptive thinking: the model decides when and how much to think.
|
|
12
22
|
class ThinkingConfigAdaptive < Type
|
|
@@ -81,15 +91,15 @@ module ClaudeAgentSDK
|
|
|
81
91
|
|
|
82
92
|
# A local plugin directory (ClaudeAgentOptions#plugins entries).
|
|
83
93
|
class SdkPluginConfig < Type
|
|
84
|
-
def initialize: (?path: String?) -> void
|
|
94
|
+
def initialize: (?path: (String | Pathname)?) -> void
|
|
85
95
|
| (Hash[Symbol | String, untyped]? attributes) -> void
|
|
86
96
|
|
|
87
|
-
attr_accessor path: String?
|
|
97
|
+
attr_accessor path: (String | Pathname)?
|
|
88
98
|
|
|
89
99
|
# "local".
|
|
90
100
|
attr_reader type: String
|
|
91
101
|
|
|
92
|
-
def to_h: () -> { type: String, path: String? }
|
|
102
|
+
def to_h: () -> { type: String, path: (String | Pathname)? }
|
|
93
103
|
end
|
|
94
104
|
|
|
95
105
|
class SandboxNetworkConfig < Type
|
|
@@ -138,6 +148,13 @@ module ClaudeAgentSDK
|
|
|
138
148
|
|
|
139
149
|
# Sandbox settings for isolated command execution
|
|
140
150
|
# (ClaudeAgentOptions#sandbox).
|
|
151
|
+
#
|
|
152
|
+
# network / filesystem take the typed config or a Hash. A Hash may spell
|
|
153
|
+
# the fields of the typed class in snake_case or in the CLI's camelCase,
|
|
154
|
+
# with Symbol or String keys; other keys are sent as written. A snake_case
|
|
155
|
+
# key is sent under the CLI's name only when its value has the shape the
|
|
156
|
+
# CLI accepts for it; otherwise it is sent as written, which the CLI
|
|
157
|
+
# ignores.
|
|
141
158
|
class SandboxSettings < Type
|
|
142
159
|
def initialize: (?enabled: bool?, ?fail_if_unavailable: bool?, ?auto_allow_bash_if_sandboxed: bool?, ?excluded_commands: Array[String]?, ?allow_unsandboxed_commands: bool?, ?network: (SandboxNetworkConfig | Hash[Symbol | String, untyped])?, ?filesystem: (SandboxFilesystemConfig | Hash[Symbol | String, untyped])?, ?ignore_violations: Hash[Symbol | String, Array[String]]?, ?enable_weaker_nested_sandbox: bool?, ?enable_weaker_network_isolation: bool?, ?ripgrep: Hash[Symbol | String, untyped]?) -> void
|
|
143
160
|
| (Hash[Symbol | String, untyped]? attributes) -> void
|
|
@@ -181,15 +198,15 @@ module ClaudeAgentSDK
|
|
|
181
198
|
|
|
182
199
|
# A system prompt loaded from a file.
|
|
183
200
|
class SystemPromptFile < Type
|
|
184
|
-
def initialize: (?path: String?) -> void
|
|
201
|
+
def initialize: (?path: (String | Pathname)?) -> void
|
|
185
202
|
| (Hash[Symbol | String, untyped]? attributes) -> void
|
|
186
203
|
|
|
187
|
-
attr_accessor path: String?
|
|
204
|
+
attr_accessor path: (String | Pathname)?
|
|
188
205
|
|
|
189
206
|
# "file".
|
|
190
207
|
attr_reader type: String
|
|
191
208
|
|
|
192
|
-
def to_h: () -> { type: String, path: String? }
|
|
209
|
+
def to_h: () -> { type: String, path: (String | Pathname)? }
|
|
193
210
|
end
|
|
194
211
|
|
|
195
212
|
# The Claude Code preset system prompt, optionally extended.
|
|
@@ -48,7 +48,7 @@ module ClaudeAgentSDK
|
|
|
48
48
|
?permission_prompt_tool_name: String?,
|
|
49
49
|
?cwd: (String | Pathname)?,
|
|
50
50
|
?cli_path: (String | Pathname)?,
|
|
51
|
-
?settings: (String | Hash[Symbol | String, untyped])?,
|
|
51
|
+
?settings: (String | Pathname | Hash[Symbol | String, untyped])?,
|
|
52
52
|
?add_dirs: Array[String | Pathname]?,
|
|
53
53
|
?env: Hash[String | Symbol, String?]?,
|
|
54
54
|
?extra_args: Hash[String | Symbol, untyped]?,
|
|
@@ -68,7 +68,7 @@ module ClaudeAgentSDK
|
|
|
68
68
|
?plugins: Array[SdkPluginConfig | Hash[Symbol | String, untyped]]?,
|
|
69
69
|
?debug_stderr: (_Puts | String)?,
|
|
70
70
|
?betas: Array[String]?,
|
|
71
|
-
?tools: (Array[String] | ToolsPreset | Hash[Symbol | String, untyped])?,
|
|
71
|
+
?tools: (Array[String] | ToolsPreset | Hash[Symbol | String, untyped] | String)?,
|
|
72
72
|
?sandbox: (SandboxSettings | Hash[Symbol | String, untyped] | bool)?,
|
|
73
73
|
?thinking: thinking_config?,
|
|
74
74
|
?effort: (String | Symbol | Integer)?,
|
|
@@ -133,8 +133,9 @@ module ClaudeAgentSDK
|
|
|
133
133
|
|
|
134
134
|
attr_accessor cli_path: (String | Pathname)?
|
|
135
135
|
|
|
136
|
-
# A settings JSON String, a path to a settings file
|
|
137
|
-
|
|
136
|
+
# A settings JSON String, a path to a settings file (a String or a
|
|
137
|
+
# Pathname), or a Hash.
|
|
138
|
+
attr_accessor settings: (String | Pathname | Hash[Symbol | String, untyped])?
|
|
138
139
|
|
|
139
140
|
attr_accessor add_dirs: Array[String | Pathname]?
|
|
140
141
|
|
|
@@ -185,10 +186,13 @@ module ClaudeAgentSDK
|
|
|
185
186
|
# Entries of SDK_BETAS.
|
|
186
187
|
attr_accessor betas: Array[String]?
|
|
187
188
|
|
|
188
|
-
# The base tool set: tool names, or
|
|
189
|
-
|
|
189
|
+
# The base tool set: tool names (an Array, or the CLI's comma-separated
|
|
190
|
+
# String such as "Read,Grep"), or a preset.
|
|
191
|
+
attr_accessor tools: (Array[String] | ToolsPreset | Hash[Symbol | String, untyped] | String)?
|
|
190
192
|
|
|
191
|
-
# true / false are forwarded as-is (a bare toggle).
|
|
193
|
+
# true / false are forwarded as-is (a bare toggle). A Hash may spell the
|
|
194
|
+
# fields of SandboxSettings, and of its network / filesystem, in
|
|
195
|
+
# snake_case or in the CLI's camelCase (see SandboxSettings).
|
|
192
196
|
attr_accessor sandbox: (SandboxSettings | Hash[Symbol | String, untyped] | bool)?
|
|
193
197
|
|
|
194
198
|
attr_accessor thinking: thinking_config?
|
|
@@ -41,7 +41,8 @@ module ClaudeAgentSDK
|
|
|
41
41
|
|
|
42
42
|
attr_accessor directories: Array[String]?
|
|
43
43
|
|
|
44
|
-
# One of PERMISSION_UPDATE_DESTINATIONS.
|
|
44
|
+
# One of PERMISSION_UPDATE_DESTINATIONS. When nil, #to_h sends "session"
|
|
45
|
+
# (unless type is nil too).
|
|
45
46
|
attr_accessor destination: String?
|
|
46
47
|
|
|
47
48
|
attr_reader rules: Array[PermissionRuleValue]?
|
|
@@ -49,7 +50,8 @@ module ClaudeAgentSDK
|
|
|
49
50
|
# Hash entries (camelCase or snake_case keys) become PermissionRuleValue.
|
|
50
51
|
def rules=: (Array[PermissionRuleValue | Hash[Symbol | String, untyped]]? value) -> Array[PermissionRuleValue | Hash[Symbol | String, untyped]]?
|
|
51
52
|
|
|
52
|
-
# The wire form (camelCase keys).
|
|
53
|
+
# The wire form (camelCase keys). A rule without content has no
|
|
54
|
+
# ruleContent key.
|
|
53
55
|
def to_h: () -> Hash[Symbol, untyped]
|
|
54
56
|
end
|
|
55
57
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ya-luotao
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: async
|
|
@@ -54,14 +54,14 @@ dependencies:
|
|
|
54
54
|
name: bundler
|
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
|
56
56
|
requirements:
|
|
57
|
-
- - "
|
|
57
|
+
- - ">="
|
|
58
58
|
- !ruby/object:Gem::Version
|
|
59
59
|
version: '2.0'
|
|
60
60
|
type: :development
|
|
61
61
|
prerelease: false
|
|
62
62
|
version_requirements: !ruby/object:Gem::Requirement
|
|
63
63
|
requirements:
|
|
64
|
-
- - "
|
|
64
|
+
- - ">="
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
66
|
version: '2.0'
|
|
67
67
|
- !ruby/object:Gem::Dependency
|
|
@@ -118,6 +118,7 @@ executables: []
|
|
|
118
118
|
extensions: []
|
|
119
119
|
extra_rdoc_files: []
|
|
120
120
|
files:
|
|
121
|
+
- ".yardopts"
|
|
121
122
|
- CHANGELOG.md
|
|
122
123
|
- LICENSE
|
|
123
124
|
- README.md
|
|
@@ -129,6 +130,7 @@ files:
|
|
|
129
130
|
- docs/hooks-and-permissions.md
|
|
130
131
|
- docs/mcp-servers.md
|
|
131
132
|
- docs/observability.md
|
|
133
|
+
- docs/options.md
|
|
132
134
|
- docs/rails.md
|
|
133
135
|
- docs/sessions.md
|
|
134
136
|
- docs/subagents.md
|