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
|
@@ -1,2536 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# Type constants for permission update destinations
|
|
17
|
-
PERMISSION_UPDATE_DESTINATIONS = %w[userSettings projectSettings localSettings session].freeze
|
|
18
|
-
|
|
19
|
-
# Type constants for permission behaviors
|
|
20
|
-
PERMISSION_BEHAVIORS = %w[allow deny ask].freeze
|
|
21
|
-
|
|
22
|
-
# Type constants for hook events
|
|
23
|
-
HOOK_EVENTS = %w[
|
|
24
|
-
PreToolUse
|
|
25
|
-
PostToolUse
|
|
26
|
-
PostToolUseFailure
|
|
27
|
-
Notification
|
|
28
|
-
UserPromptSubmit
|
|
29
|
-
SessionStart
|
|
30
|
-
SessionEnd
|
|
31
|
-
Stop
|
|
32
|
-
StopFailure
|
|
33
|
-
SubagentStart
|
|
34
|
-
SubagentStop
|
|
35
|
-
PreCompact
|
|
36
|
-
PostCompact
|
|
37
|
-
PermissionRequest
|
|
38
|
-
PermissionDenied
|
|
39
|
-
Setup
|
|
40
|
-
TeammateIdle
|
|
41
|
-
TaskCreated
|
|
42
|
-
TaskCompleted
|
|
43
|
-
Elicitation
|
|
44
|
-
ElicitationResult
|
|
45
|
-
ConfigChange
|
|
46
|
-
WorktreeCreate
|
|
47
|
-
WorktreeRemove
|
|
48
|
-
InstructionsLoaded
|
|
49
|
-
CwdChanged
|
|
50
|
-
FileChanged
|
|
51
|
-
].freeze
|
|
52
|
-
|
|
53
|
-
# Type constants for assistant message errors
|
|
54
|
-
ASSISTANT_MESSAGE_ERRORS = %w[authentication_failed billing_error rate_limit invalid_request server_error max_output_tokens unknown].freeze
|
|
55
|
-
|
|
56
|
-
# Type constants for SDK beta features
|
|
57
|
-
# Available beta features that can be enabled via the betas option
|
|
58
|
-
SDK_BETAS = %w[context-1m-2025-08-07].freeze
|
|
59
|
-
|
|
60
|
-
# Base class for all types.
|
|
61
|
-
class Type
|
|
62
|
-
def self.wrap(object)
|
|
63
|
-
return object if object.is_a?(self)
|
|
64
|
-
return nil if object.nil?
|
|
65
|
-
|
|
66
|
-
new(object)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def self.from_hash(hash)
|
|
70
|
-
return unless hash.is_a?(Hash)
|
|
71
|
-
|
|
72
|
-
new(hash)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def initialize(attributes = {})
|
|
76
|
-
assign_attributes(attributes) if attributes
|
|
77
|
-
super()
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def [](name)
|
|
81
|
-
read_attribute(name)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def []=(name, value)
|
|
85
|
-
assign_attribute(name, value)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Subclasses should override this to return a hash representation of the object.
|
|
89
|
-
def to_h
|
|
90
|
-
{}
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# The copy hook used wherever ClaudeAgentOptions are copied (dup_with and
|
|
94
|
-
# the configured-defaults merge). Identity by default: most Type instances
|
|
95
|
-
# are messages or callback payloads that never live inside options, and a
|
|
96
|
-
# user-supplied object that does (an observer, a store adapter) must stay
|
|
97
|
-
# the same object. Option VALUE types include OptionValue to opt in to
|
|
98
|
-
# copying, so a per-session change to e.g. sandbox rules can never reach
|
|
99
|
-
# another session or the configured defaults.
|
|
100
|
-
def dup_for_options
|
|
101
|
-
self
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Mixed into the mutable value types that ClaudeAgentOptions holds
|
|
105
|
-
# (SandboxSettings, SystemPromptPreset, AgentDefinition, ...). The copy
|
|
106
|
-
# recurses into the value's own state with Type.deep_dup_for_options, so
|
|
107
|
-
# nested containers and nested value types (SandboxSettings#network) are
|
|
108
|
-
# copied too while identity leaves (McpSdkServerConfig#instance, the
|
|
109
|
-
# callables in HookMatcher#hooks) stay shared. #dup never copies frozen
|
|
110
|
-
# state, so a copy of a frozen value (the configured-defaults snapshot) is
|
|
111
|
-
# mutable.
|
|
112
|
-
module OptionValue
|
|
113
|
-
def dup_for_options
|
|
114
|
-
copy = dup
|
|
115
|
-
copy.instance_variables.each do |ivar|
|
|
116
|
-
copy.instance_variable_set(ivar, Type.deep_dup_for_options(copy.instance_variable_get(ivar)))
|
|
117
|
-
end
|
|
118
|
-
copy
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Recurse into Hash/Array containers and option value types, and copy
|
|
123
|
-
# mutable (unfrozen) Strings — a caller-built prompt, model or
|
|
124
|
-
# allowed_tools entry is as much shared state as an Array, and `str <<
|
|
125
|
-
# 'x'` on one copy would otherwise change every other. A frozen String
|
|
126
|
-
# (any literal under frozen_string_literal) is immutable and keeps
|
|
127
|
-
# identity. Every other leaf keeps object identity (observer factories,
|
|
128
|
-
# callbacks, SDK MCP server instances must not be duped). Rebuild
|
|
129
|
-
# containers via dup.clear (never Hash#to_h / Array#map) to preserve
|
|
130
|
-
# container SUBCLASSES: to_h flattens e.g. Rails'
|
|
131
|
-
# HashWithIndifferentAccess into a plain Hash, silently breaking symbol
|
|
132
|
-
# lookups on the copy (config[:type] == 'sdk' → nil). Hash keys: Ruby
|
|
133
|
-
# already stores a dup'd, frozen copy of an unfrozen plain String key, but
|
|
134
|
-
# not of a String SUBCLASS key, so those are copied (and frozen, as keys
|
|
135
|
-
# should be) here. A compare_by_identity Hash is left keyed by the
|
|
136
|
-
# caller's objects: copying a key would break the caller's own lookups.
|
|
137
|
-
def self.deep_dup_for_options(value)
|
|
138
|
-
case value
|
|
139
|
-
when Hash
|
|
140
|
-
copy = value.dup.clear
|
|
141
|
-
value.each { |k, v| copy[option_hash_key(k, value)] = deep_dup_for_options(v) }
|
|
142
|
-
copy
|
|
143
|
-
when Array
|
|
144
|
-
copy = value.dup.clear
|
|
145
|
-
value.each { |v| copy << deep_dup_for_options(v) }
|
|
146
|
-
copy
|
|
147
|
-
when Type then value.dup_for_options
|
|
148
|
-
when String then value.frozen? ? value : value.dup # #dup keeps subclass and encoding
|
|
149
|
-
else value
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def self.option_hash_key(key, hash)
|
|
154
|
-
return key unless key.is_a?(String) && !key.frozen? && !hash.compare_by_identity?
|
|
155
|
-
|
|
156
|
-
key.dup.freeze
|
|
157
|
-
end
|
|
158
|
-
private_class_method :option_hash_key
|
|
159
|
-
|
|
160
|
-
# Bounded, human-oriented #inspect listing the non-nil instance variables
|
|
161
|
-
# in definition order:
|
|
162
|
-
#
|
|
163
|
-
# #<ClaudeAgentSDK::ResultMessage subtype="success" num_turns=3 ...>
|
|
164
|
-
#
|
|
165
|
-
# Messages carry whole transcripts, tool payloads and usage maps, so the
|
|
166
|
-
# output is bounded rather than faithful: long Strings are truncated,
|
|
167
|
-
# long Arrays/Hashes abbreviated, and nesting past INSPECT_MAX_DEPTH (or
|
|
168
|
-
# a reference cycle) collapses to a placeholder. Other objects keep their
|
|
169
|
-
# own #inspect (truncated) unless they only have Kernel#inspect, which
|
|
170
|
-
# dumps every ivar recursively — those (SDK MCP server instances, store
|
|
171
|
-
# adapters, observers) show as `#<ClassName>`. For display only: nothing
|
|
172
|
-
# sent to the CLI goes through #inspect or #to_s (wire output uses #to_h).
|
|
173
|
-
def inspect
|
|
174
|
-
inspect_with(0, {}.compare_by_identity)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Object#to_s ignores instance variables, so `puts message` would print
|
|
178
|
-
# only a class name and an address. Types with a natural textual form
|
|
179
|
-
# (UserMessage, AssistantMessage, TextBlock, ResultMessage, SystemMessage)
|
|
180
|
-
# override this.
|
|
181
|
-
def to_s
|
|
182
|
-
inspect
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Declares attributes that carry credentials (env vars, auth headers).
|
|
186
|
-
# Objects get logged, so #inspect shows them filtered; #to_h and
|
|
187
|
-
# everything sent to the CLI are unaffected. Inherited by subclasses.
|
|
188
|
-
def self.inspect_filtered(*names)
|
|
189
|
-
@inspect_filtered_attributes = (inspect_filtered_attributes + names.map(&:to_s)).uniq.freeze
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def self.inspect_filtered_attributes
|
|
193
|
-
@inspect_filtered_attributes || (superclass <= Type ? superclass.inspect_filtered_attributes : [].freeze)
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
INSPECT_MAX_STRING = 80
|
|
197
|
-
INSPECT_MAX_ITEMS = 5
|
|
198
|
-
INSPECT_MAX_DEPTH = 2
|
|
199
|
-
private_constant :INSPECT_MAX_STRING, :INSPECT_MAX_ITEMS, :INSPECT_MAX_DEPTH
|
|
200
|
-
|
|
201
|
-
protected
|
|
202
|
-
|
|
203
|
-
# `seen` holds the Types/containers on the current rendering path (not
|
|
204
|
-
# every one rendered so far), so a shared-but-acyclic value still renders
|
|
205
|
-
# in full wherever it appears.
|
|
206
|
-
def inspect_with(depth, seen)
|
|
207
|
-
return "#<#{inspect_class_name} …>" if depth > INSPECT_MAX_DEPTH || seen.key?(self)
|
|
208
|
-
|
|
209
|
-
seen[self] = true
|
|
210
|
-
begin
|
|
211
|
-
attributes = inspect_attributes.map do |name, value|
|
|
212
|
-
" #{name}=#{inspect_bounded(value, depth + 1, seen)}"
|
|
213
|
-
end
|
|
214
|
-
"#<#{inspect_class_name}#{attributes.join}>"
|
|
215
|
-
ensure
|
|
216
|
-
seen.delete(self)
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
private
|
|
221
|
-
|
|
222
|
-
# [name, value] pairs rendered by #inspect. Subclasses override to hide
|
|
223
|
-
# redundant state or redact secrets — never by mutating the object.
|
|
224
|
-
def inspect_attributes
|
|
225
|
-
filtered = self.class.inspect_filtered_attributes
|
|
226
|
-
instance_variables.filter_map do |ivar|
|
|
227
|
-
value = instance_variable_get(ivar)
|
|
228
|
-
next if value.nil?
|
|
229
|
-
|
|
230
|
-
name = ivar.to_s.delete_prefix('@')
|
|
231
|
-
[name, filtered.include?(name) ? inspect_filter(value) : value]
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# A credential-bearing Hash keeps its keys (useful when debugging which
|
|
236
|
-
# variables are set) with every value replaced; anything else is replaced
|
|
237
|
-
# outright. Builds a new Hash; the object itself is never touched.
|
|
238
|
-
def inspect_filter(value)
|
|
239
|
-
value.respond_to?(:each_key) ? value.each_key.to_h { |key| [key, '[FILTERED]'] } : '[FILTERED]'
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def inspect_class_name
|
|
243
|
-
self.class.name || self.class.inspect
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def inspect_bounded(value, depth, seen)
|
|
247
|
-
case value
|
|
248
|
-
when Type then value.inspect_with(depth, seen)
|
|
249
|
-
when String then inspect_truncated(value)
|
|
250
|
-
when Array then inspect_container(value, '[', ']', depth, seen) { |item| inspect_bounded(item, depth + 1, seen) }
|
|
251
|
-
when Hash
|
|
252
|
-
inspect_container(value, '{', '}', depth, seen) do |key, item|
|
|
253
|
-
"#{inspect_hash_key(key, depth + 1, seen)}#{inspect_bounded(item, depth + 1, seen)}"
|
|
254
|
-
end
|
|
255
|
-
when Proc, Method, UnboundMethod then inspect_callable(value)
|
|
256
|
-
else inspect_leaf(value)
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
def inspect_container(value, open, close, depth, seen, &)
|
|
261
|
-
return "#{open}#{close}" if value.empty?
|
|
262
|
-
return "#{open}…(#{value.size})#{close}" if depth > INSPECT_MAX_DEPTH || seen.key?(value)
|
|
263
|
-
|
|
264
|
-
seen[value] = true
|
|
265
|
-
begin
|
|
266
|
-
parts = value.first(INSPECT_MAX_ITEMS).map(&)
|
|
267
|
-
parts << "…(+#{value.size - INSPECT_MAX_ITEMS} more)" if value.size > INSPECT_MAX_ITEMS
|
|
268
|
-
"#{open}#{parts.join(', ')}#{close}"
|
|
269
|
-
ensure
|
|
270
|
-
seen.delete(value)
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
# Rendered by hand rather than via Hash#inspect, whose format differs
|
|
275
|
-
# between Ruby 3.3 (`{:a=>1}`) and 3.4 (`{a: 1}`).
|
|
276
|
-
def inspect_hash_key(key, depth, seen)
|
|
277
|
-
return "#{key.name}: " if key.is_a?(Symbol) && key.inspect.match?(/\A:\w+[?!]?\z/)
|
|
278
|
-
|
|
279
|
-
"#{inspect_bounded(key, depth, seen)} => "
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def inspect_truncated(string)
|
|
283
|
-
return string.inspect if string.length <= INSPECT_MAX_STRING
|
|
284
|
-
|
|
285
|
-
"#{string[0, INSPECT_MAX_STRING].inspect}…(+#{string.length - INSPECT_MAX_STRING} chars)"
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
# Callbacks (can_use_tool, hooks, callback_wrapper, ...) are user-supplied:
|
|
289
|
-
# render them from source_location rather than their own #inspect, which
|
|
290
|
-
# a subclass may override (and raise from) and which embeds an absolute
|
|
291
|
-
# path — `#<Proc(lambda) permissions.rb:17>`, `#<Method Policy#call>`.
|
|
292
|
-
def inspect_callable(value)
|
|
293
|
-
label = if value.is_a?(Proc)
|
|
294
|
-
value.lambda? ? 'Proc(lambda)' : 'Proc'
|
|
295
|
-
else
|
|
296
|
-
"#{value.class.name} #{value.owner.name || value.owner.inspect}##{value.name}"
|
|
297
|
-
end
|
|
298
|
-
file, line = value.source_location
|
|
299
|
-
rendered = file ? "#<#{label} #{File.basename(file)}:#{line}>" : "#<#{label}>"
|
|
300
|
-
inspect_truncated_text(rendered)
|
|
301
|
-
rescue StandardError
|
|
302
|
-
inspect_leaf(value)
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def inspect_truncated_text(rendered)
|
|
306
|
-
return rendered if rendered.length <= INSPECT_MAX_STRING
|
|
307
|
-
|
|
308
|
-
"#{rendered[0, INSPECT_MAX_STRING]}…(+#{rendered.length - INSPECT_MAX_STRING} chars)"
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# Printing must never raise (it runs inside loggers and `puts`), so an
|
|
312
|
-
# object whose #inspect raises, or a BasicObject without one, falls back
|
|
313
|
-
# to a placeholder.
|
|
314
|
-
def inspect_leaf(value)
|
|
315
|
-
return "#<#{value.class}>" if kernel_inspect_only?(value)
|
|
316
|
-
|
|
317
|
-
inspect_truncated_text(value.inspect)
|
|
318
|
-
rescue StandardError
|
|
319
|
-
begin
|
|
320
|
-
"#<#{value.class}>"
|
|
321
|
-
rescue StandardError
|
|
322
|
-
'#<?>'
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
def kernel_inspect_only?(value)
|
|
327
|
-
Kernel.instance_method(:method).bind_call(value, :inspect).owner == Kernel
|
|
328
|
-
rescue TypeError # not a Kernel object: BasicObject, Delegator
|
|
329
|
-
false
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
# Allow camelCase attribute access
|
|
333
|
-
def method_missing(method_name, ...)
|
|
334
|
-
normalized = normalize_name(method_name)
|
|
335
|
-
|
|
336
|
-
if normalized != method_name.to_s && respond_to?(normalized)
|
|
337
|
-
public_send(normalized, ...)
|
|
338
|
-
else
|
|
339
|
-
super
|
|
340
|
-
end
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
def respond_to_missing?(method_name, include_private = false)
|
|
344
|
-
normalized = normalize_name(method_name)
|
|
345
|
-
(normalized != method_name.to_s && respond_to?(normalized)) || super
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def assign_attributes(attributes)
|
|
349
|
-
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument, #{attributes.inspect} passed." unless attributes.respond_to?(:each_pair)
|
|
350
|
-
|
|
351
|
-
return if attributes.empty?
|
|
352
|
-
|
|
353
|
-
attributes.each_pair { |name, value| assign_attribute(name, value) }
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def assign_attribute(name, value)
|
|
357
|
-
setter = :"#{normalize_name(name)}="
|
|
358
|
-
public_send(setter, value) if respond_to?(setter)
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def read_attribute(name)
|
|
362
|
-
getter = normalize_name(name)
|
|
363
|
-
public_send(getter) if respond_to?(getter)
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
def normalize_name(name)
|
|
367
|
-
name = name.to_s.dup
|
|
368
|
-
name.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, "_")
|
|
369
|
-
name.tr!("-", "_")
|
|
370
|
-
name.downcase!
|
|
371
|
-
name
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
FALSE_VALUES = [
|
|
375
|
-
false, 0,
|
|
376
|
-
"0", :'0',
|
|
377
|
-
"f", :f,
|
|
378
|
-
"F", :F,
|
|
379
|
-
"false", :false, # rubocop:disable Lint/BooleanSymbol
|
|
380
|
-
"FALSE", :FALSE,
|
|
381
|
-
"off", :off,
|
|
382
|
-
"OFF", :OFF
|
|
383
|
-
].to_set.freeze
|
|
384
|
-
|
|
385
|
-
private_constant :FALSE_VALUES
|
|
386
|
-
|
|
387
|
-
def coerce_boolean(value)
|
|
388
|
-
return if value.nil?
|
|
389
|
-
|
|
390
|
-
if value == ""
|
|
391
|
-
nil
|
|
392
|
-
else
|
|
393
|
-
!FALSE_VALUES.include?(value)
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
# Content Blocks
|
|
399
|
-
|
|
400
|
-
# Text content block
|
|
401
|
-
class TextBlock < Type
|
|
402
|
-
attr_accessor :text
|
|
403
|
-
|
|
404
|
-
def to_s
|
|
405
|
-
text.to_s
|
|
406
|
-
end
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
# Thinking content block
|
|
410
|
-
class ThinkingBlock < Type
|
|
411
|
-
attr_accessor :thinking, :signature
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
# Tool use content block
|
|
415
|
-
class ToolUseBlock < Type
|
|
416
|
-
attr_accessor :id, :name, :input
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
# Tool result content block
|
|
420
|
-
class ToolResultBlock < Type
|
|
421
|
-
attr_accessor :tool_use_id, :content, :is_error
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
# Server-side tool use (CLI's built-in tools that execute server-side
|
|
425
|
-
# rather than as MCP tools — advisor, web_search, code_execution, etc.).
|
|
426
|
-
# Mirrors Python's `ServerToolUseBlock`.
|
|
427
|
-
class ServerToolUseBlock < Type
|
|
428
|
-
attr_accessor :id, :name, :input
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
# Result of a server-side tool execution. Mirrors Python's
|
|
432
|
-
# `ServerToolResultBlock`.
|
|
433
|
-
class ServerToolResultBlock < Type
|
|
434
|
-
attr_accessor :tool_use_id, :content, :is_error
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
# Generic content block for types the SDK doesn't explicitly handle (e.g., "document", "image").
|
|
438
|
-
# Preserves the raw hash data for forward compatibility with newer CLI versions.
|
|
439
|
-
class UnknownBlock < Type
|
|
440
|
-
attr_accessor :type, :data
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
# Deferred tool use, emitted on `ResultMessage` when a PreToolUse hook
|
|
444
|
-
# returned `permissionDecision: "defer"`. The session can be resumed later
|
|
445
|
-
# to execute the deferred call. Mirrors Python's `DeferredToolUse`.
|
|
446
|
-
class DeferredToolUse < Type
|
|
447
|
-
attr_accessor :id, :name, :input
|
|
448
|
-
end
|
|
449
|
-
|
|
450
|
-
# Message Types
|
|
451
|
-
|
|
452
|
-
# User message
|
|
453
|
-
class UserMessage < Type
|
|
454
|
-
attr_accessor :content, :uuid, :parent_tool_use_id, :tool_use_result
|
|
455
|
-
|
|
456
|
-
# Provenance of this message — where the turn came from.
|
|
457
|
-
#
|
|
458
|
-
# In streaming-input mode a single connection interleaves the turns you
|
|
459
|
-
# send with turns the session injects on its own (background-task
|
|
460
|
-
# notifications, fired scheduled-task prompts, MCP channel messages,
|
|
461
|
-
# messages relayed from peer sessions, ...). `origin` tells them apart —
|
|
462
|
-
# see {ResultMessage#origin} for deciding whether a result answers *your*
|
|
463
|
-
# prompt.
|
|
464
|
-
#
|
|
465
|
-
# **Key form — read this before indexing into it.** A plain Hash, passed
|
|
466
|
-
# through from the CLI untouched: the SDK does not model it, whitelist its
|
|
467
|
-
# keys, or rewrite them, so kinds and fields newer CLI versions add stay
|
|
468
|
-
# visible. Keys therefore follow the transport's JSON parsing, which uses
|
|
469
|
-
# `symbolize_names: true` — they are **Symbols with the wire spelling
|
|
470
|
-
# preserved**, so camelCase keys stay camelCase and you index with
|
|
471
|
-
# `origin[:kind]`, `origin[:fromSession]`, `origin[:senderTaskId]`,
|
|
472
|
-
# `origin[:verifiedPeerPid]`. This is unlike the snake_case attributes
|
|
473
|
-
# elsewhere in this SDK, and unlike the Python SDK's string keys: a
|
|
474
|
-
# `origin["kind"]` or `origin[:from_session]` lookup silently returns nil
|
|
475
|
-
# and makes every attributed turn look unattributed. Only `:kind` is
|
|
476
|
-
# guaranteed present; the rest depend on it.
|
|
477
|
-
#
|
|
478
|
-
# `nil` means the CLI did not attribute the message — that is the normal
|
|
479
|
-
# case for prompts you send through {ClaudeAgentSDK.query} / {Client#query},
|
|
480
|
-
# unless the host stamps `origin: { kind: 'human' }` on the message Hash
|
|
481
|
-
# itself (only the `human` kind is honored from an SDK host). Populated on
|
|
482
|
-
# injected turns (task notifications, channel/peer messages, ...) and on
|
|
483
|
-
# user messages the CLI replays; tool-result messages never carry it.
|
|
484
|
-
#
|
|
485
|
-
# Known `:kind` values — documentation, not validation; treat anything
|
|
486
|
-
# unrecognized as "not human":
|
|
487
|
-
#
|
|
488
|
-
# - `'human'` — a turn submitted by the SDK host
|
|
489
|
-
# - `'channel'` — arrived on an MCP channel; `:server` names the MCP server
|
|
490
|
-
# - `'peer'` — relayed from a peer session. `:from` (sender address,
|
|
491
|
-
# sender-asserted — for reply routing or display, never as proof of
|
|
492
|
-
# identity), `:name` (display name, already normalized by the CLI),
|
|
493
|
-
# `:fromSession` (the sender's host-openable session id, a navigation
|
|
494
|
-
# target only), `:senderTaskId` (task id of the in-process background
|
|
495
|
-
# subagent that sent it; absent for cross-session peers), `:body`
|
|
496
|
-
# (decoded message body with the peer envelope stripped, byte-exact with
|
|
497
|
-
# what the model saw — render this instead of re-parsing the message
|
|
498
|
-
# text), `:verifiedPeerPid` (kernel-verified pid of the process that
|
|
499
|
-
# connected to this session's local messaging socket — the *connecting*
|
|
500
|
-
# process, which for relayed traffic is the relay; absent when
|
|
501
|
-
# unverifiable)
|
|
502
|
-
# - `'task-notification'` — a background task's delivery. `:subkind` is
|
|
503
|
-
# `'scheduled-trigger'` (the fired prompt of a scheduled task) or
|
|
504
|
-
# `'peer-send-message'` (a message sent from another of the user's
|
|
505
|
-
# sessions); absent for ordinary background-task notifications
|
|
506
|
-
# - `'coordinator'`, `'unclassified'`, `'observer'` (`:from` /
|
|
507
|
-
# `:senderTaskId` as for `peer`), `'auto-continuation'`,
|
|
508
|
-
# `'observer-activity'`
|
|
509
|
-
#
|
|
510
|
-
# @return [Hash{Symbol => Object}, nil]
|
|
511
|
-
# @see ResultMessage#origin
|
|
512
|
-
attr_accessor :origin
|
|
513
|
-
|
|
514
|
-
# Concatenated text of this message. Handles both String content
|
|
515
|
-
# (plain-text user prompt) and Array-of-blocks content (typed content).
|
|
516
|
-
# Returns "" when there is no text.
|
|
517
|
-
def text
|
|
518
|
-
case content
|
|
519
|
-
when String then content
|
|
520
|
-
when Array then content.grep(TextBlock).map(&:text).join("\n\n")
|
|
521
|
-
else ''
|
|
522
|
-
end
|
|
523
|
-
end
|
|
524
|
-
|
|
525
|
-
alias to_s text
|
|
526
|
-
end
|
|
527
|
-
|
|
528
|
-
# Assistant message with content blocks
|
|
529
|
-
class AssistantMessage < Type
|
|
530
|
-
attr_accessor :content, :model, :parent_tool_use_id, :error, :usage,
|
|
531
|
-
:message_id, :stop_reason, :session_id, :uuid
|
|
532
|
-
|
|
533
|
-
# Concatenated text across every TextBlock in this message's content.
|
|
534
|
-
# Returns "" when the message has no text (e.g., a pure tool_use turn).
|
|
535
|
-
def text
|
|
536
|
-
Array(content).grep(TextBlock).map(&:text).join("\n\n")
|
|
537
|
-
end
|
|
538
|
-
|
|
539
|
-
alias to_s text
|
|
540
|
-
end
|
|
541
|
-
|
|
542
|
-
# System message with metadata.
|
|
543
|
-
# When constructed from a raw CLI hash, the whole hash is stored in `#data`
|
|
544
|
-
# unless the caller explicitly provides a `:data` entry.
|
|
545
|
-
class SystemMessage < Type
|
|
546
|
-
attr_accessor :subtype, :data
|
|
547
|
-
|
|
548
|
-
def initialize(attributes = {})
|
|
549
|
-
super
|
|
550
|
-
@data ||= attributes if attributes.is_a?(Hash)
|
|
551
|
-
end
|
|
552
|
-
|
|
553
|
-
def to_s
|
|
554
|
-
subtype.nil? ? '[system]' : "[system: #{subtype}]"
|
|
555
|
-
end
|
|
556
|
-
|
|
557
|
-
private
|
|
558
|
-
|
|
559
|
-
# A typed subclass (InitMessage, TaskStartedMessage, ...) already exposes
|
|
560
|
-
# the fields of its raw frame as attributes; repeating @data would double
|
|
561
|
-
# the output. A bare SystemMessage (unrecognized subtype) keeps it, since
|
|
562
|
-
# @data is the only place its payload lives.
|
|
563
|
-
def inspect_attributes
|
|
564
|
-
return super if instance_of?(SystemMessage)
|
|
565
|
-
|
|
566
|
-
super.reject { |pair| pair.first == 'data' }
|
|
567
|
-
end
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
# Init system message (emitted at session start and after /clear)
|
|
571
|
-
class InitMessage < SystemMessage
|
|
572
|
-
attr_accessor :uuid, :session_id, :agents, :api_key_source, :betas,
|
|
573
|
-
:claude_code_version, :cwd, :tools, :mcp_servers, :model,
|
|
574
|
-
:permission_mode, :slash_commands, :output_style, :skills, :plugins,
|
|
575
|
-
:fast_mode_state # "off", "cooldown", or "on"
|
|
576
|
-
end
|
|
577
|
-
|
|
578
|
-
# Compact boundary system message (emitted after context compaction completes)
|
|
579
|
-
class CompactBoundaryMessage < SystemMessage
|
|
580
|
-
attr_accessor :uuid, :session_id
|
|
581
|
-
attr_reader :compact_metadata
|
|
582
|
-
|
|
583
|
-
def compact_metadata=(value)
|
|
584
|
-
@compact_metadata = value.is_a?(Hash) ? CompactMetadata.new(value) : value
|
|
585
|
-
end
|
|
586
|
-
end
|
|
587
|
-
|
|
588
|
-
# Metadata about a compaction event
|
|
589
|
-
class CompactMetadata < Type
|
|
590
|
-
attr_accessor :pre_tokens, :post_tokens, :trigger, :custom_instructions, :preserved_segment
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
# Status system message (compacting status, permission mode changes)
|
|
594
|
-
class StatusMessage < SystemMessage
|
|
595
|
-
attr_accessor :uuid, :session_id, :status, :permission_mode
|
|
596
|
-
end
|
|
597
|
-
|
|
598
|
-
# API retry system message
|
|
599
|
-
class APIRetryMessage < SystemMessage
|
|
600
|
-
attr_accessor :uuid, :session_id, :attempt, :max_retries, :retry_delay_ms, :error_status, :error
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
# Local command output system message
|
|
604
|
-
class LocalCommandOutputMessage < SystemMessage
|
|
605
|
-
attr_accessor :uuid, :session_id, :content
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
# Emitted when a session_store mirror batch fails terminally and is
|
|
609
|
-
# dropped — timeouts immediately (never retried), other failures after up
|
|
610
|
-
# to three attempts. The local-disk transcript is still durable; this is
|
|
611
|
-
# the consumer's only signal that the external store missed a batch
|
|
612
|
-
# (at-most-once delivery).
|
|
613
|
-
class MirrorErrorMessage < SystemMessage
|
|
614
|
-
attr_accessor :uuid, :session_id, :error, :key
|
|
615
|
-
end
|
|
616
|
-
|
|
617
|
-
# Hook started system message
|
|
618
|
-
class HookStartedMessage < SystemMessage
|
|
619
|
-
attr_accessor :uuid, :session_id, :hook_id, :hook_name, :hook_event
|
|
620
|
-
end
|
|
621
|
-
|
|
622
|
-
# Hook progress system message
|
|
623
|
-
class HookProgressMessage < SystemMessage
|
|
624
|
-
attr_accessor :uuid, :session_id, :hook_id, :hook_name, :hook_event, :stdout, :stderr, :output
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
# Hook response system message
|
|
628
|
-
class HookResponseMessage < SystemMessage
|
|
629
|
-
attr_accessor :uuid, :session_id, :hook_id, :hook_name, :hook_event,
|
|
630
|
-
:output, :stdout, :stderr, :exit_code,
|
|
631
|
-
:outcome # "success", "error", or "cancelled"
|
|
632
|
-
end
|
|
633
|
-
|
|
634
|
-
# Session state changed system message
|
|
635
|
-
class SessionStateChangedMessage < SystemMessage
|
|
636
|
-
attr_accessor :uuid, :session_id,
|
|
637
|
-
:state # "idle", "running", or "requires_action"
|
|
638
|
-
end
|
|
639
|
-
|
|
640
|
-
# Files persisted system message
|
|
641
|
-
class FilesPersistedMessage < SystemMessage
|
|
642
|
-
attr_accessor :uuid, :session_id, :files, :failed, :processed_at
|
|
643
|
-
end
|
|
644
|
-
|
|
645
|
-
# Elicitation complete system message
|
|
646
|
-
class ElicitationCompleteMessage < SystemMessage
|
|
647
|
-
attr_accessor :uuid, :session_id, :mcp_server_name, :elicitation_id
|
|
648
|
-
end
|
|
649
|
-
|
|
650
|
-
# Task lifecycle notification statuses
|
|
651
|
-
TASK_NOTIFICATION_STATUSES = %w[completed failed stopped].freeze
|
|
652
|
-
|
|
653
|
-
# Possible status values reported inside a `task_updated` patch.
|
|
654
|
-
# pending/running/paused are non-terminal; completed/failed/killed are
|
|
655
|
-
# terminal. Note: task_updated reports the raw "killed"; the CLI maps that to
|
|
656
|
-
# "stopped" only when it emits a task_notification.
|
|
657
|
-
TASK_UPDATED_STATUSES = %w[pending running paused completed failed killed].freeze
|
|
658
|
-
|
|
659
|
-
# Task statuses that mean the task has finished and should be cleared from any
|
|
660
|
-
# "active task" tracking. Spans both lifecycle vocabularies: task_notification
|
|
661
|
-
# reports "stopped" (the CLI's mapped form of a killed task) while task_updated
|
|
662
|
-
# reports the raw "killed". Treat the status of a TaskNotificationMessage and a
|
|
663
|
-
# TaskUpdatedMessage the same way.
|
|
664
|
-
TERMINAL_TASK_STATUSES = %w[completed failed stopped killed].freeze
|
|
665
|
-
|
|
666
|
-
# Typed usage data for task progress and notifications
|
|
667
|
-
class TaskUsage < Type
|
|
668
|
-
attr_accessor :total_tokens, :tool_uses, :duration_ms
|
|
669
|
-
|
|
670
|
-
def initialize(attributes = {})
|
|
671
|
-
super
|
|
672
|
-
@total_tokens ||= 0
|
|
673
|
-
@tool_uses ||= 0
|
|
674
|
-
@duration_ms ||= 0
|
|
675
|
-
end
|
|
676
|
-
end
|
|
677
|
-
|
|
678
|
-
# Task started system message (subagent/background task started)
|
|
679
|
-
class TaskStartedMessage < SystemMessage
|
|
680
|
-
attr_accessor :task_id, :description, :uuid, :session_id, :tool_use_id, :task_type,
|
|
681
|
-
:workflow_name, :prompt,
|
|
682
|
-
:subagent_type # Subagent type, for Task/Agent tool subagents; nil otherwise
|
|
683
|
-
|
|
684
|
-
# Whether the task was registered in the background (`true`) or in the
|
|
685
|
-
# foreground with the spawning tool call blocking on it (`false`). `nil`
|
|
686
|
-
# means the CLI did not say (the field is optional, and only set for
|
|
687
|
-
# `local_agent` and `local_bash` tasks) — so test `== false`, never
|
|
688
|
-
# falsiness, to detect a foreground/blocking task. A resumed subagent is
|
|
689
|
-
# always registered in the background. A later move to the background does
|
|
690
|
-
# not re-emit task_started; it arrives as {TaskUpdatedMessage#is_backgrounded}.
|
|
691
|
-
#
|
|
692
|
-
# @return [Boolean, nil]
|
|
693
|
-
attr_accessor :is_backgrounded
|
|
694
|
-
|
|
695
|
-
# Nesting depth of a spawned subagent (`local_agent`) task: 1 for a
|
|
696
|
-
# top-level spawn, N+1 when spawned from inside a depth-N agent. `nil` on
|
|
697
|
-
# other task types and on CLIs that do not report it.
|
|
698
|
-
#
|
|
699
|
-
# @return [Integer, nil]
|
|
700
|
-
attr_accessor :spawn_depth
|
|
701
|
-
|
|
702
|
-
# Display flags, passed through for the host to act on — the SDK never
|
|
703
|
-
# filters frames or computes activity from them. Both are optional
|
|
704
|
-
# Booleans: `nil` when absent, an explicit `false` preserved.
|
|
705
|
-
#
|
|
706
|
-
# - `skip_transcript`: an ambient/housekeeping task. Hide it from the
|
|
707
|
-
# inline transcript; it may still appear in a tasks panel.
|
|
708
|
-
# - `ambient`: true for tasks that are not activity — every
|
|
709
|
-
# `skip_transcript` task, plus every live-update watcher (requested or
|
|
710
|
-
# auto-started). Exclude these from activity indicators.
|
|
711
|
-
#
|
|
712
|
-
# @return [Boolean, nil]
|
|
713
|
-
attr_accessor :skip_transcript, :ambient
|
|
714
|
-
end
|
|
715
|
-
|
|
716
|
-
# Task progress system message (periodic update from a running task).
|
|
717
|
-
#
|
|
718
|
-
# `summary` is an optional one-line status for the task's row — `nil` on any
|
|
719
|
-
# frame that lacks one. For a `local_agent` task it is the model-generated
|
|
720
|
-
# progress summary, which the CLI produces only while generation is enabled
|
|
721
|
-
# (see {ClaudeAgentOptions#agent_progress_summaries}); for a backgrounded
|
|
722
|
-
# `mcp_task` it is the MCP server's own status message and needs no option.
|
|
723
|
-
class TaskProgressMessage < SystemMessage
|
|
724
|
-
attr_accessor :task_id, :description, :usage, :uuid, :session_id, :tool_use_id, :last_tool_name, :summary,
|
|
725
|
-
:subagent_type # Subagent type, for Task/Agent tool subagents; nil otherwise
|
|
726
|
-
end
|
|
727
|
-
|
|
728
|
-
# Task notification system message (task completed/failed/stopped).
|
|
729
|
-
#
|
|
730
|
-
# Note: not every terminal task emits this message. Background tasks may
|
|
731
|
-
# instead report completion only via a TaskUpdatedMessage whose patch["status"]
|
|
732
|
-
# is terminal (see TERMINAL_TASK_STATUSES). Consumers tracking active task IDs
|
|
733
|
-
# should clear them on a terminal status from *either* message.
|
|
734
|
-
class TaskNotificationMessage < SystemMessage
|
|
735
|
-
attr_accessor :task_id, :status, :output_file, :summary, :uuid, :session_id, :tool_use_id, :usage
|
|
736
|
-
|
|
737
|
-
# Machine-readable cause, set only when the task did not end through an
|
|
738
|
-
# ordinary completion, failure, or stop. The one known value is
|
|
739
|
-
# `'worker_restart'` (the worker process restarted and the resumed process
|
|
740
|
-
# found the task orphaned; always with status `'stopped'`). Documentation,
|
|
741
|
-
# not validation: newer CLIs may add values.
|
|
742
|
-
#
|
|
743
|
-
# @return [String, nil]
|
|
744
|
-
attr_accessor :reason
|
|
745
|
-
|
|
746
|
-
# For a backgrounded MCP task (`task_type: 'mcp_task'`) that completed: the
|
|
747
|
-
# `resource_link` content blocks of its final result — the files it
|
|
748
|
-
# returned by reference. A backgrounded task's tool_result is placeholder
|
|
749
|
-
# text, so this is where a host learns which files the call produced; join
|
|
750
|
-
# to the originating call via `tool_use_id`. `nil` when the result had
|
|
751
|
-
# none or the task is any other type.
|
|
752
|
-
#
|
|
753
|
-
# Passed through from the CLI untouched, so each element is a Hash whose
|
|
754
|
-
# keys are **Symbols with the wire spelling preserved**: `:uri` and `:name`
|
|
755
|
-
# (Strings, always present), and optionally `:title`, `:description`,
|
|
756
|
-
# `:mimeType` (camelCase — a `:mime_type` lookup returns nil), `:size` (a
|
|
757
|
-
# Number, not necessarily an Integer), `:annotations` (a Hash of arbitrary
|
|
758
|
-
# values). Elements carry no `type: 'resource_link'` discriminator. The CLI
|
|
759
|
-
# describes its own output as at most 50 links / 64 KiB serialized; that is
|
|
760
|
-
# a producer-side note, and the SDK neither enforces nor truncates.
|
|
761
|
-
#
|
|
762
|
-
# @return [Array<Hash{Symbol => Object}>, nil]
|
|
763
|
-
attr_accessor :resource_links
|
|
764
|
-
|
|
765
|
-
# Display flags with the same meaning as on {TaskStartedMessage}:
|
|
766
|
-
# `skip_transcript` (hide from the inline transcript) and `ambient` (not
|
|
767
|
-
# activity — exclude from activity indicators). Optional Booleans: `nil`
|
|
768
|
-
# when absent, an explicit `false` preserved. The SDK does not act on them.
|
|
769
|
-
#
|
|
770
|
-
# @return [Boolean, nil]
|
|
771
|
-
attr_accessor :skip_transcript, :ambient
|
|
772
|
-
end
|
|
773
|
-
|
|
774
|
-
# Task updated system message (background task lifecycle state change).
|
|
775
|
-
#
|
|
776
|
-
# The CLI emits system/task_updated events as a task moves through its
|
|
777
|
-
# lifecycle. `patch` carries the changed fields (e.g. status, end_time); when
|
|
778
|
-
# patch["status"] is terminal (see TERMINAL_TASK_STATUSES) the task has
|
|
779
|
-
# finished. A background task's terminal state can arrive *only* as a
|
|
780
|
-
# TaskUpdatedMessage with no accompanying TaskNotificationMessage — e.g. a task
|
|
781
|
-
# stopped via TaskStop reports status "killed" here and the matching
|
|
782
|
-
# notification is sometimes suppressed. Consumers tracking active task IDs
|
|
783
|
-
# should clear them on a terminal status from *either* message.
|
|
784
|
-
#
|
|
785
|
-
# Parsed defensively in the constructor — a lifecycle event must never raise:
|
|
786
|
-
# `status` is derived from patch["status"] (not a top-level field); a non-Hash
|
|
787
|
-
# or absent patch falls back to {}; and `task_id` defaults to "" (never nil,
|
|
788
|
-
# matching the Python SDK) so consumers can rely on it always being a String.
|
|
789
|
-
# The full patch is preserved on `#patch` for callers that need more than the
|
|
790
|
-
# derived readers.
|
|
791
|
-
#
|
|
792
|
-
# A patch carries only the fields that changed, so every derived reader is
|
|
793
|
-
# `nil` when its field is absent. That matters most for `is_backgrounded`:
|
|
794
|
-
# `true` means the task just moved to the background (e.g. after
|
|
795
|
-
# {Client#background_tasks}), while `nil` means "this patch does not mention
|
|
796
|
-
# it" — not "foreground". Merge patches into your own task map rather than
|
|
797
|
-
# reading any single one as the task's full state.
|
|
798
|
-
class TaskUpdatedMessage < SystemMessage
|
|
799
|
-
attr_accessor :task_id, :patch, :status, :uuid, :session_id,
|
|
800
|
-
:description, # patch[:description] — String, nil when unchanged
|
|
801
|
-
:error, # patch[:error] — String, nil when unchanged
|
|
802
|
-
:end_time, # patch[:end_time] — Integer (epoch ms), nil when unchanged
|
|
803
|
-
:total_paused_ms, # patch[:total_paused_ms] — Integer, nil when unchanged
|
|
804
|
-
:is_backgrounded # patch[:is_backgrounded] — true/false, nil when unchanged
|
|
805
|
-
|
|
806
|
-
def initialize(attributes = {})
|
|
807
|
-
super
|
|
808
|
-
@task_id ||= ''
|
|
809
|
-
@patch = {} unless @patch.is_a?(Hash)
|
|
810
|
-
@status = patch_value(:status)
|
|
811
|
-
@description = patch_value(:description)
|
|
812
|
-
@error = patch_value(:error)
|
|
813
|
-
@end_time = patch_value(:end_time)
|
|
814
|
-
@total_paused_ms = patch_value(:total_paused_ms)
|
|
815
|
-
@is_backgrounded = patch_value(:is_backgrounded)
|
|
816
|
-
end
|
|
817
|
-
|
|
818
|
-
private
|
|
819
|
-
|
|
820
|
-
# The parser always hands over a symbol-keyed patch; a hand-built message
|
|
821
|
-
# may use string keys. `fetch` with a block (not `||`) keeps an explicit
|
|
822
|
-
# `false` from falling through to the string-key lookup's nil.
|
|
823
|
-
def patch_value(key)
|
|
824
|
-
@patch.fetch(key) { @patch[key.to_s] }
|
|
825
|
-
end
|
|
826
|
-
end
|
|
827
|
-
|
|
828
|
-
# Background tasks changed system message: the full set of live background
|
|
829
|
-
# tasks, emitted whenever membership changes (start, completion, kill, a
|
|
830
|
-
# foreground agent being backgrounded) or an entry's `ambient` flag flips.
|
|
831
|
-
#
|
|
832
|
-
# A **level** signal with **REPLACE semantics** — `tasks` is every live
|
|
833
|
-
# background task after the change, so swap your set for each payload rather
|
|
834
|
-
# than pairing task_started / task_notification edges; a missed edge then
|
|
835
|
-
# cannot wedge a stale "running" indicator. Per the CLI's contract:
|
|
836
|
-
#
|
|
837
|
-
# - Ordering relative to the edge frames for the same transition is
|
|
838
|
-
# unspecified, and the payload carries ids only — do not correlate it
|
|
839
|
-
# with the edge stream.
|
|
840
|
-
# - The level is per-process: nothing is emitted at startup, so reset to the
|
|
841
|
-
# empty set whenever the session's CLI process (re)starts.
|
|
842
|
-
# - `tasks: []` is an authoritative empty snapshot for that process, not a
|
|
843
|
-
# missing value.
|
|
844
|
-
# - A repeated `initialize` on an already-running process is answered with a
|
|
845
|
-
# snapshot of the current set (even an empty one) right behind its success
|
|
846
|
-
# response; older CLIs send nothing there. This SDK initializes once per
|
|
847
|
-
# connection, so that only matters to custom transports that reconnect.
|
|
848
|
-
# - It covers *background* tasks only. A foreground subagent (the spawning
|
|
849
|
-
# tool call still blocking) is not listed until it is backgrounded.
|
|
850
|
-
#
|
|
851
|
-
# `tasks` is passed through untouched: an Array of symbol-keyed Hashes
|
|
852
|
-
# `{ task_id:, task_type:, description:, ambient: }`. `:ambient` is optional;
|
|
853
|
-
# true marks tasks that are not activity (housekeeping, live-update
|
|
854
|
-
# watchers), which hosts should exclude from activity indicators.
|
|
855
|
-
#
|
|
856
|
-
# The SDK itself deliberately does not consume this frame for its own
|
|
857
|
-
# stdin-close bookkeeping; it is typed purely for consumers.
|
|
858
|
-
class BackgroundTasksChangedMessage < SystemMessage
|
|
859
|
-
attr_accessor :tasks, :uuid, :session_id
|
|
860
|
-
end
|
|
861
|
-
|
|
862
|
-
# Permission denied system message: a tool call was auto-denied without an
|
|
863
|
-
# interactive permission prompt (auto-mode classifier, dontAsk mode,
|
|
864
|
-
# headless-agent auto-deny, a deny rule, or — with no can_use_tool callback —
|
|
865
|
-
# an "ask" decision that nobody can answer). The "ask" path with a callback
|
|
866
|
-
# surfaces through can_use_tool instead.
|
|
867
|
-
#
|
|
868
|
-
# **Best-effort advisory, not a complete denial feed**:
|
|
869
|
-
# {ResultMessage#permission_denials} is the authoritative record. In rare
|
|
870
|
-
# races a booked denial has no frame, or a frame has no booked denial — so do
|
|
871
|
-
# not derive counts or permission state from this stream. Not covered at all:
|
|
872
|
-
# PreToolUse hook denies, deny-rule overrides of a hook's allow/ask decision,
|
|
873
|
-
# Read/Edit/Write calls refused by a path-scoped deny rule (all resolve before
|
|
874
|
-
# the permission check), and the MCP `--permission-prompt-tool` surface.
|
|
875
|
-
#
|
|
876
|
-
# `agent_id` is a subagent id for host-side routing; it is NOT a permission
|
|
877
|
-
# `request_id`, and this message is not a pending permission request.
|
|
878
|
-
# `decision_reason_type` is an open String (the values below are examples,
|
|
879
|
-
# not an enum). The CLI's `decision_reason_code` is marked internal and is
|
|
880
|
-
# left to `#data` with no stability promise.
|
|
881
|
-
class PermissionDeniedMessage < SystemMessage
|
|
882
|
-
attr_accessor :uuid, :session_id, :tool_name, :tool_use_id,
|
|
883
|
-
:agent_id, # Subagent ID when the denied call originated inside a subagent; nil otherwise
|
|
884
|
-
:decision_reason_type, # Open String, e.g. "classifier", "asyncAgent", "mode", "rule"; nil when not reported
|
|
885
|
-
:decision_reason, # Human-readable reason from the deciding component; nil when not reported
|
|
886
|
-
:message # The rejection message returned to the model in the tool_result
|
|
887
|
-
end
|
|
888
|
-
|
|
889
|
-
# Result message with cost and usage information
|
|
890
|
-
class ResultMessage < Type
|
|
891
|
-
# model_usage maps model name => per-model usage Hash, passed through
|
|
892
|
-
# verbatim from the CLI's modelUsage field, so its keys are camelCase
|
|
893
|
-
# (matches the TypeScript/Python SDKs' ModelUsage shape): inputTokens,
|
|
894
|
-
# outputTokens, cacheReadInputTokens, cacheCreationInputTokens,
|
|
895
|
-
# webSearchRequests, costUSD, contextWindow, maxOutputTokens, plus
|
|
896
|
-
# optional canonicalModel (canonical id used for the pricing lookup —
|
|
897
|
-
# may differ from the raw model-string key for provider-specific
|
|
898
|
-
# ids/aliases) and provider ('firstParty', 'bedrock', 'vertex', ...).
|
|
899
|
-
#
|
|
900
|
-
# terminal_reason says why the query loop ended ("completed",
|
|
901
|
-
# "max_turns", "aborted_streaming", ...). "aborted_streaming" /
|
|
902
|
-
# "aborted_tools" mean the turn was cancelled via Client#interrupt (an
|
|
903
|
-
# interrupt control request). nil when the CLI did not report one
|
|
904
|
-
# (older CLI versions, or a result that bypassed the query loop such
|
|
905
|
-
# as a local slash command).
|
|
906
|
-
attr_accessor :subtype, :duration_ms, :duration_api_ms, :is_error,
|
|
907
|
-
:num_turns, :session_id, :stop_reason, :total_cost_usd, :usage,
|
|
908
|
-
:result, :structured_output,
|
|
909
|
-
:model_usage, # Hash of { model_name => usage_data }, see above
|
|
910
|
-
:permission_denials, # Array of { tool_name:, tool_use_id:, tool_input: }
|
|
911
|
-
:errors, # Array of error strings (present on error subtypes)
|
|
912
|
-
:uuid,
|
|
913
|
-
:fast_mode_state, # "off", "cooldown", or "on"
|
|
914
|
-
:api_error_status, # Integer HTTP status (429, 500, 529) on api_error subtype (CLI 2.1.110+)
|
|
915
|
-
:terminal_reason # why the query loop ended, see above
|
|
916
|
-
|
|
917
|
-
attr_reader :deferred_tool_use # DeferredToolUse, populated when a PreToolUse hook deferred
|
|
918
|
-
|
|
919
|
-
def deferred_tool_use=(value)
|
|
920
|
-
@deferred_tool_use = value.is_a?(Hash) ? DeferredToolUse.from_hash(value) : value
|
|
921
|
-
end
|
|
922
|
-
|
|
923
|
-
# Provenance of the user message that triggered this turn — `nil` when the
|
|
924
|
-
# CLI did not attribute it. Lets a streaming-input consumer distinguish the
|
|
925
|
-
# result of its own prompt from the result of a turn the session injected
|
|
926
|
-
# on its own:
|
|
927
|
-
#
|
|
928
|
-
# if result.origin.nil? || result.origin[:kind] == 'human'
|
|
929
|
-
# # a turn this application submitted
|
|
930
|
-
# elsif result.origin[:kind] == 'task-notification'
|
|
931
|
-
# # follow-up turn driven by a background task
|
|
932
|
-
# end
|
|
933
|
-
#
|
|
934
|
-
# **Key form.** A plain Hash passed through from the CLI untouched, so its
|
|
935
|
-
# keys are **Symbols with the wire spelling preserved** — camelCase stays
|
|
936
|
-
# camelCase (`origin[:kind]`, `origin[:fromSession]`,
|
|
937
|
-
# `origin[:verifiedPeerPid]`), unlike the snake_case attributes elsewhere
|
|
938
|
-
# in this SDK and unlike the Python SDK's string keys. Indexing with
|
|
939
|
-
# `origin["kind"]` silently returns nil and makes every attributed turn
|
|
940
|
-
# look unattributed.
|
|
941
|
-
#
|
|
942
|
-
# See {UserMessage#origin} for the full list of known `:kind` values and
|
|
943
|
-
# their per-kind keys.
|
|
944
|
-
#
|
|
945
|
-
# @return [Hash{Symbol => Object}, nil]
|
|
946
|
-
# @see UserMessage#origin
|
|
947
|
-
attr_accessor :origin
|
|
948
|
-
|
|
949
|
-
# One human-readable line, e.g. `[result: success, 3 turns, 4.2s, $0.0120]`
|
|
950
|
-
# (parts the CLI did not report are left out). An error result appends its
|
|
951
|
-
# `errors`. Use #inspect for every field.
|
|
952
|
-
def to_s
|
|
953
|
-
parts = [subtype].compact
|
|
954
|
-
parts << "#{num_turns} #{num_turns == 1 ? 'turn' : 'turns'}" unless num_turns.nil?
|
|
955
|
-
parts << format('%.1fs', duration_ms / 1000.0) if duration_ms.is_a?(Numeric)
|
|
956
|
-
parts << format('$%.4f', total_cost_usd) if total_cost_usd.is_a?(Numeric)
|
|
957
|
-
line = parts.empty? ? '[result]' : "[result: #{parts.join(', ')}]"
|
|
958
|
-
line += " - #{Array(errors).join('; ')}" if is_error && !Array(errors).empty?
|
|
959
|
-
line
|
|
960
|
-
end
|
|
961
|
-
end
|
|
962
|
-
|
|
963
|
-
# Stream event for partial message updates
|
|
964
|
-
class StreamEvent < Type
|
|
965
|
-
attr_accessor :uuid, :session_id, :event, :parent_tool_use_id
|
|
966
|
-
end
|
|
967
|
-
|
|
968
|
-
# Tool progress message (type: 'tool_progress')
|
|
969
|
-
class ToolProgressMessage < Type
|
|
970
|
-
attr_accessor :uuid, :session_id, :tool_use_id, :tool_name, :parent_tool_use_id,
|
|
971
|
-
:elapsed_time_seconds, :task_id
|
|
972
|
-
end
|
|
973
|
-
|
|
974
|
-
# Auth status message (type: 'auth_status')
|
|
975
|
-
class AuthStatusMessage < Type
|
|
976
|
-
attr_accessor :uuid, :session_id, :is_authenticating, :output, :error
|
|
977
|
-
end
|
|
978
|
-
|
|
979
|
-
# Tool use summary message (type: 'tool_use_summary')
|
|
980
|
-
class ToolUseSummaryMessage < Type
|
|
981
|
-
attr_accessor :uuid, :session_id, :summary, :preceding_tool_use_ids
|
|
982
|
-
end
|
|
983
|
-
|
|
984
|
-
# Prompt suggestion message (type: 'prompt_suggestion')
|
|
985
|
-
class PromptSuggestionMessage < Type
|
|
986
|
-
attr_accessor :uuid, :session_id, :suggestion
|
|
987
|
-
end
|
|
988
|
-
|
|
989
|
-
# Type constants for rate limit statuses
|
|
990
|
-
RATE_LIMIT_STATUSES = %w[allowed allowed_warning rejected].freeze
|
|
991
|
-
|
|
992
|
-
# Type constants for rate limit types
|
|
993
|
-
RATE_LIMIT_TYPES = %w[five_hour seven_day seven_day_opus seven_day_sonnet overage].freeze
|
|
994
|
-
|
|
995
|
-
# Rate limit info with typed fields
|
|
996
|
-
class RateLimitInfo < Type
|
|
997
|
-
attr_accessor :status, :resets_at, :rate_limit_type, :utilization,
|
|
998
|
-
:overage_status, :overage_resets_at, :overage_disabled_reason, :raw
|
|
999
|
-
|
|
1000
|
-
def initialize(attributes = {})
|
|
1001
|
-
super
|
|
1002
|
-
@raw ||= {}
|
|
1003
|
-
end
|
|
1004
|
-
end
|
|
1005
|
-
|
|
1006
|
-
# Rate limit event emitted when rate limit info changes
|
|
1007
|
-
class RateLimitEvent < Type
|
|
1008
|
-
attr_accessor :uuid, :session_id, :raw_data
|
|
1009
|
-
attr_reader :rate_limit_info
|
|
1010
|
-
|
|
1011
|
-
def initialize(attributes = {})
|
|
1012
|
-
super
|
|
1013
|
-
@rate_limit_info ||= RateLimitInfo.new
|
|
1014
|
-
end
|
|
1015
|
-
|
|
1016
|
-
def rate_limit_info=(value)
|
|
1017
|
-
@rate_limit_info = value.is_a?(Hash) ? RateLimitInfo.new(value.merge(raw: value)) : value
|
|
1018
|
-
end
|
|
1019
|
-
|
|
1020
|
-
# Backward-compatible accessor returning the full raw event payload
|
|
1021
|
-
def data
|
|
1022
|
-
@raw_data || {}
|
|
1023
|
-
end
|
|
1024
|
-
end
|
|
1025
|
-
|
|
1026
|
-
# Emitted when the session's conversation is replaced without ending the
|
|
1027
|
-
# connection — e.g. after `/clear` or any other flow that discards the
|
|
1028
|
-
# transcript mid-session (type: 'conversation_reset').
|
|
1029
|
-
#
|
|
1030
|
-
# In streaming-input mode a single connection carries many user turns, and a
|
|
1031
|
-
# reset clears the conversation history *and* zeroes the running totals
|
|
1032
|
-
# reported on subsequent {ResultMessage} objects (e.g. `total_cost_usd`). If
|
|
1033
|
-
# you accumulate those totals across a long-lived session, snapshot them when
|
|
1034
|
-
# this message arrives.
|
|
1035
|
-
#
|
|
1036
|
-
# @!attribute [rw] new_conversation_id
|
|
1037
|
-
# Opaque identifier for the fresh conversation, for UIs to key an empty
|
|
1038
|
-
# transcript on (and to discard any cached session title). This is *not*
|
|
1039
|
-
# the `session_id` of subsequent messages — read that from the next
|
|
1040
|
-
# message.
|
|
1041
|
-
# @return [String]
|
|
1042
|
-
# @!attribute [rw] uuid
|
|
1043
|
-
# Unique ID of this message.
|
|
1044
|
-
# @return [String]
|
|
1045
|
-
# @!attribute [rw] session_id
|
|
1046
|
-
# ID of the session that was reset (the outgoing session; messages after
|
|
1047
|
-
# the reset carry a new `session_id`).
|
|
1048
|
-
# @return [String]
|
|
1049
|
-
class ConversationResetMessage < Type
|
|
1050
|
-
attr_accessor :new_conversation_id, :uuid, :session_id
|
|
1051
|
-
end
|
|
1052
|
-
|
|
1053
|
-
# Thinking configuration types
|
|
1054
|
-
#
|
|
1055
|
-
# `display` controls how thinking content appears in responses. Valid values
|
|
1056
|
-
# are `"summarized"` (plaintext summary) and `"omitted"` (empty thinking
|
|
1057
|
-
# field, signature only). Defaults are model-dependent: Opus 4.6/Sonnet 4.6
|
|
1058
|
-
# default to `"summarized"`; Opus 4.7 and every later model default to
|
|
1059
|
-
# `"omitted"`. Pass `display: "summarized"` explicitly on those to get
|
|
1060
|
-
# visible thinking text. Not supported with `ThinkingConfigDisabled`.
|
|
1061
|
-
THINKING_DISPLAY_VALUES = %w[summarized omitted].freeze
|
|
1062
|
-
|
|
1063
|
-
# Adaptive thinking: the model decides when and how much to think
|
|
1064
|
-
# (sent as `--thinking adaptive`, no budget); control depth with `effort`.
|
|
1065
|
-
class ThinkingConfigAdaptive < Type
|
|
1066
|
-
include Type::OptionValue
|
|
1067
|
-
|
|
1068
|
-
attr_reader :type, :display
|
|
1069
|
-
|
|
1070
|
-
def initialize(attributes = {})
|
|
1071
|
-
super
|
|
1072
|
-
@type = 'adaptive'
|
|
1073
|
-
end
|
|
1074
|
-
|
|
1075
|
-
def display=(value)
|
|
1076
|
-
@display = validate_display(value)
|
|
1077
|
-
end
|
|
1078
|
-
|
|
1079
|
-
private
|
|
1080
|
-
|
|
1081
|
-
def validate_display(value)
|
|
1082
|
-
return nil if value.nil?
|
|
1083
|
-
return value if THINKING_DISPLAY_VALUES.include?(value.to_s)
|
|
1084
|
-
|
|
1085
|
-
raise ArgumentError,
|
|
1086
|
-
"invalid thinking display #{value.inspect}; expected one of #{THINKING_DISPLAY_VALUES.inspect}"
|
|
1087
|
-
end
|
|
1088
|
-
end
|
|
1089
|
-
|
|
1090
|
-
# Enabled thinking: uses a user-specified budget
|
|
1091
|
-
class ThinkingConfigEnabled < Type
|
|
1092
|
-
include Type::OptionValue
|
|
1093
|
-
|
|
1094
|
-
attr_accessor :budget_tokens
|
|
1095
|
-
attr_reader :type, :display
|
|
1096
|
-
|
|
1097
|
-
def initialize(attributes = {})
|
|
1098
|
-
super
|
|
1099
|
-
@type = 'enabled'
|
|
1100
|
-
end
|
|
1101
|
-
|
|
1102
|
-
def display=(value)
|
|
1103
|
-
@display = validate_display(value)
|
|
1104
|
-
end
|
|
1105
|
-
|
|
1106
|
-
private
|
|
1107
|
-
|
|
1108
|
-
def validate_display(value)
|
|
1109
|
-
return nil if value.nil?
|
|
1110
|
-
return value if THINKING_DISPLAY_VALUES.include?(value.to_s)
|
|
1111
|
-
|
|
1112
|
-
raise ArgumentError,
|
|
1113
|
-
"invalid thinking display #{value.inspect}; expected one of #{THINKING_DISPLAY_VALUES.inspect}"
|
|
1114
|
-
end
|
|
1115
|
-
end
|
|
1116
|
-
|
|
1117
|
-
# Disabled thinking: sets thinking tokens to 0
|
|
1118
|
-
class ThinkingConfigDisabled < Type
|
|
1119
|
-
include Type::OptionValue
|
|
1120
|
-
|
|
1121
|
-
attr_reader :type
|
|
1122
|
-
|
|
1123
|
-
def initialize(attributes = {})
|
|
1124
|
-
super
|
|
1125
|
-
@type = 'disabled'
|
|
1126
|
-
end
|
|
1127
|
-
end
|
|
1128
|
-
|
|
1129
|
-
# Agent definition configuration
|
|
1130
|
-
class AgentDefinition < Type
|
|
1131
|
-
include Type::OptionValue
|
|
1132
|
-
|
|
1133
|
-
attr_accessor :description, :prompt, :tools, :disallowed_tools, :model, :skills, :memory, :mcp_servers,
|
|
1134
|
-
:initial_prompt, :max_turns, :background, :effort, :permission_mode
|
|
1135
|
-
end
|
|
1136
|
-
|
|
1137
|
-
# Permission rule value
|
|
1138
|
-
class PermissionRuleValue < Type
|
|
1139
|
-
attr_accessor :tool_name, :rule_content
|
|
1140
|
-
end
|
|
1141
|
-
|
|
1142
|
-
# Permission update configuration
|
|
1143
|
-
class PermissionUpdate < Type
|
|
1144
|
-
attr_accessor :type, :behavior, :mode, :directories, :destination
|
|
1145
|
-
attr_reader :rules
|
|
1146
|
-
|
|
1147
|
-
# Wire-format parity with Python PermissionUpdate.from_dict (#920): the CLI
|
|
1148
|
-
# sends rules as camelCase hashes ({toolName:, ruleContent:}); hydrate them
|
|
1149
|
-
# into PermissionRuleValue (Type#assign_attribute normalizes the camelCase
|
|
1150
|
-
# keys). Already-typed PermissionRuleValue entries pass through unchanged.
|
|
1151
|
-
def rules=(value)
|
|
1152
|
-
@rules = value&.map { |rule| rule.is_a?(Hash) ? PermissionRuleValue.new(rule) : rule }
|
|
1153
|
-
end
|
|
1154
|
-
|
|
1155
|
-
def to_h
|
|
1156
|
-
result = { type: @type }
|
|
1157
|
-
result[:destination] = @destination if @destination
|
|
1158
|
-
|
|
1159
|
-
case @type
|
|
1160
|
-
when 'addRules', 'replaceRules', 'removeRules'
|
|
1161
|
-
if @rules
|
|
1162
|
-
result[:rules] = @rules.map do |rule|
|
|
1163
|
-
{
|
|
1164
|
-
toolName: rule.tool_name,
|
|
1165
|
-
ruleContent: rule.rule_content
|
|
1166
|
-
}
|
|
1167
|
-
end
|
|
1168
|
-
end
|
|
1169
|
-
result[:behavior] = @behavior if @behavior
|
|
1170
|
-
when 'setMode'
|
|
1171
|
-
result[:mode] = @mode if @mode
|
|
1172
|
-
when 'addDirectories', 'removeDirectories'
|
|
1173
|
-
result[:directories] = @directories if @directories
|
|
1174
|
-
end
|
|
1175
|
-
|
|
1176
|
-
result
|
|
1177
|
-
end
|
|
1178
|
-
end
|
|
1179
|
-
|
|
1180
|
-
# Tool permission context delivered to `can_use_tool` callbacks.
|
|
1181
|
-
# CLI 2.1.110+ began populating the four pre-formatted display fields
|
|
1182
|
-
# (`title`, `display_name`, `description`, `blocked_path`,
|
|
1183
|
-
# `decision_reason`) so the SDK consumer can render the same prompt UI
|
|
1184
|
-
# the CLI would have shown. Older fields (`signal`, `suggestions`,
|
|
1185
|
-
# `tool_use_id`, `agent_id`) remain unchanged.
|
|
1186
|
-
# `signal` is a CancellationSignal on dispatched callbacks; `request_id`
|
|
1187
|
-
# identifies this permission request (distinct from the tool invocation).
|
|
1188
|
-
class ToolPermissionContext < Type
|
|
1189
|
-
attr_accessor :signal, :request_id, :suggestions, :tool_use_id, :agent_id,
|
|
1190
|
-
:title, :display_name, :description,
|
|
1191
|
-
:blocked_path, :decision_reason
|
|
1192
|
-
|
|
1193
|
-
def initialize(attributes = {})
|
|
1194
|
-
super
|
|
1195
|
-
@suggestions ||= []
|
|
1196
|
-
end
|
|
1197
|
-
end
|
|
1198
|
-
|
|
1199
|
-
# Permission results
|
|
1200
|
-
class PermissionResultAllow < Type
|
|
1201
|
-
attr_accessor :updated_input, :updated_permissions
|
|
1202
|
-
attr_reader :behavior
|
|
1203
|
-
|
|
1204
|
-
def initialize(attributes = {})
|
|
1205
|
-
super
|
|
1206
|
-
@behavior = 'allow'
|
|
1207
|
-
end
|
|
1208
|
-
end
|
|
1209
|
-
|
|
1210
|
-
class PermissionResultDeny < Type
|
|
1211
|
-
attr_accessor :message, :interrupt
|
|
1212
|
-
attr_reader :behavior
|
|
1213
|
-
|
|
1214
|
-
def initialize(attributes = {})
|
|
1215
|
-
super
|
|
1216
|
-
@behavior = 'deny'
|
|
1217
|
-
@message ||= ''
|
|
1218
|
-
@interrupt = false if @interrupt.nil?
|
|
1219
|
-
end
|
|
1220
|
-
end
|
|
1221
|
-
|
|
1222
|
-
# Hook matcher configuration
|
|
1223
|
-
class HookMatcher < Type
|
|
1224
|
-
include Type::OptionValue
|
|
1225
|
-
|
|
1226
|
-
attr_accessor :matcher, :hooks, :timeout
|
|
1227
|
-
|
|
1228
|
-
def initialize(attributes = {})
|
|
1229
|
-
super
|
|
1230
|
-
@hooks ||= []
|
|
1231
|
-
end
|
|
1232
|
-
end
|
|
1233
|
-
|
|
1234
|
-
# Hook context passed to hook callbacks. Dispatched hooks receive the control
|
|
1235
|
-
# request ID and a CancellationSignal (including on HookMatcher timeout).
|
|
1236
|
-
class HookContext < Type
|
|
1237
|
-
attr_accessor :signal, :request_id
|
|
1238
|
-
end
|
|
1239
|
-
|
|
1240
|
-
# Base hook input with common fields
|
|
1241
|
-
class BaseHookInput < Type
|
|
1242
|
-
attr_accessor :session_id, :transcript_path, :cwd, :permission_mode
|
|
1243
|
-
attr_reader :hook_event_name
|
|
1244
|
-
end
|
|
1245
|
-
|
|
1246
|
-
# PreToolUse hook input
|
|
1247
|
-
class PreToolUseHookInput < BaseHookInput
|
|
1248
|
-
attr_accessor :tool_name, :tool_input, :tool_use_id, :agent_id, :agent_type
|
|
1249
|
-
|
|
1250
|
-
def initialize(attributes = {})
|
|
1251
|
-
super
|
|
1252
|
-
@hook_event_name = 'PreToolUse'
|
|
1253
|
-
end
|
|
1254
|
-
end
|
|
1255
|
-
|
|
1256
|
-
# PostToolUse hook input
|
|
1257
|
-
class PostToolUseHookInput < BaseHookInput
|
|
1258
|
-
attr_accessor :tool_name, :tool_input, :tool_response, :tool_use_id, :agent_id, :agent_type
|
|
1259
|
-
|
|
1260
|
-
def initialize(attributes = {})
|
|
1261
|
-
super
|
|
1262
|
-
@hook_event_name = 'PostToolUse'
|
|
1263
|
-
end
|
|
1264
|
-
end
|
|
1265
|
-
|
|
1266
|
-
# UserPromptSubmit hook input
|
|
1267
|
-
class UserPromptSubmitHookInput < BaseHookInput
|
|
1268
|
-
attr_accessor :prompt
|
|
1269
|
-
|
|
1270
|
-
def initialize(attributes = {})
|
|
1271
|
-
super
|
|
1272
|
-
@hook_event_name = 'UserPromptSubmit'
|
|
1273
|
-
end
|
|
1274
|
-
end
|
|
1275
|
-
|
|
1276
|
-
# Stop hook input
|
|
1277
|
-
# Snapshot arrays are passed through unchanged. nil means unavailable;
|
|
1278
|
-
# [] means the CLI provided an empty snapshot. They cover the parent
|
|
1279
|
-
# session's background work, NOT all foreground and background agents.
|
|
1280
|
-
class StopHookInput < BaseHookInput
|
|
1281
|
-
attr_accessor :stop_hook_active, :last_assistant_message, :background_tasks, :session_crons
|
|
1282
|
-
|
|
1283
|
-
def initialize(attributes = {})
|
|
1284
|
-
super
|
|
1285
|
-
@hook_event_name = 'Stop'
|
|
1286
|
-
@stop_hook_active = false if @stop_hook_active.nil?
|
|
1287
|
-
end
|
|
1288
|
-
end
|
|
1289
|
-
|
|
1290
|
-
# SubagentStop hook input
|
|
1291
|
-
class SubagentStopHookInput < BaseHookInput
|
|
1292
|
-
attr_accessor :stop_hook_active, :agent_id, :agent_transcript_path, :agent_type,
|
|
1293
|
-
:last_assistant_message, :background_tasks, :session_crons
|
|
1294
|
-
|
|
1295
|
-
def initialize(attributes = {})
|
|
1296
|
-
super
|
|
1297
|
-
@hook_event_name = 'SubagentStop'
|
|
1298
|
-
@stop_hook_active = false if @stop_hook_active.nil?
|
|
1299
|
-
end
|
|
1300
|
-
end
|
|
1301
|
-
|
|
1302
|
-
# PostToolUseFailure hook input
|
|
1303
|
-
class PostToolUseFailureHookInput < BaseHookInput
|
|
1304
|
-
attr_accessor :tool_name, :tool_input, :tool_use_id, :error, :is_interrupt,
|
|
1305
|
-
:agent_id, :agent_type
|
|
1306
|
-
|
|
1307
|
-
def initialize(attributes = {})
|
|
1308
|
-
super
|
|
1309
|
-
@hook_event_name = 'PostToolUseFailure'
|
|
1310
|
-
end
|
|
1311
|
-
end
|
|
1312
|
-
|
|
1313
|
-
# Notification hook input
|
|
1314
|
-
class NotificationHookInput < BaseHookInput
|
|
1315
|
-
attr_accessor :message, :title, :notification_type
|
|
1316
|
-
|
|
1317
|
-
def initialize(attributes = {})
|
|
1318
|
-
super
|
|
1319
|
-
@hook_event_name = 'Notification'
|
|
1320
|
-
end
|
|
1321
|
-
end
|
|
1322
|
-
|
|
1323
|
-
# SubagentStart hook input
|
|
1324
|
-
class SubagentStartHookInput < BaseHookInput
|
|
1325
|
-
attr_accessor :agent_id, :agent_type
|
|
1326
|
-
|
|
1327
|
-
def initialize(attributes = {})
|
|
1328
|
-
super
|
|
1329
|
-
@hook_event_name = 'SubagentStart'
|
|
1330
|
-
end
|
|
1331
|
-
end
|
|
1332
|
-
|
|
1333
|
-
# PermissionRequest hook input
|
|
1334
|
-
class PermissionRequestHookInput < BaseHookInput
|
|
1335
|
-
attr_accessor :tool_name, :tool_input, :permission_suggestions, :agent_id, :agent_type
|
|
1336
|
-
|
|
1337
|
-
def initialize(attributes = {})
|
|
1338
|
-
super
|
|
1339
|
-
@hook_event_name = 'PermissionRequest'
|
|
1340
|
-
end
|
|
1341
|
-
end
|
|
1342
|
-
|
|
1343
|
-
# PreCompact hook input
|
|
1344
|
-
class PreCompactHookInput < BaseHookInput
|
|
1345
|
-
attr_accessor :trigger, :custom_instructions
|
|
1346
|
-
|
|
1347
|
-
def initialize(attributes = {})
|
|
1348
|
-
super
|
|
1349
|
-
@hook_event_name = 'PreCompact'
|
|
1350
|
-
end
|
|
1351
|
-
end
|
|
1352
|
-
|
|
1353
|
-
# SessionStart hook input
|
|
1354
|
-
class SessionStartHookInput < BaseHookInput
|
|
1355
|
-
attr_accessor :source, :agent_type, :model
|
|
1356
|
-
|
|
1357
|
-
def initialize(attributes = {})
|
|
1358
|
-
super
|
|
1359
|
-
@hook_event_name = 'SessionStart'
|
|
1360
|
-
end
|
|
1361
|
-
end
|
|
1362
|
-
|
|
1363
|
-
# SessionEnd hook input
|
|
1364
|
-
class SessionEndHookInput < BaseHookInput
|
|
1365
|
-
attr_accessor :reason
|
|
1366
|
-
|
|
1367
|
-
def initialize(attributes = {})
|
|
1368
|
-
super
|
|
1369
|
-
@hook_event_name = 'SessionEnd'
|
|
1370
|
-
end
|
|
1371
|
-
end
|
|
1372
|
-
|
|
1373
|
-
# Setup hook input
|
|
1374
|
-
class SetupHookInput < BaseHookInput
|
|
1375
|
-
attr_accessor :trigger
|
|
1376
|
-
|
|
1377
|
-
def initialize(attributes = {})
|
|
1378
|
-
super
|
|
1379
|
-
@hook_event_name = 'Setup'
|
|
1380
|
-
end
|
|
1381
|
-
end
|
|
1382
|
-
|
|
1383
|
-
# TeammateIdle hook input
|
|
1384
|
-
class TeammateIdleHookInput < BaseHookInput
|
|
1385
|
-
attr_accessor :teammate_name, :team_name
|
|
1386
|
-
|
|
1387
|
-
def initialize(attributes = {})
|
|
1388
|
-
super
|
|
1389
|
-
@hook_event_name = 'TeammateIdle'
|
|
1390
|
-
end
|
|
1391
|
-
end
|
|
1392
|
-
|
|
1393
|
-
# TaskCompleted hook input
|
|
1394
|
-
class TaskCompletedHookInput < BaseHookInput
|
|
1395
|
-
attr_accessor :task_id, :task_subject, :task_description, :teammate_name, :team_name
|
|
1396
|
-
|
|
1397
|
-
def initialize(attributes = {})
|
|
1398
|
-
super
|
|
1399
|
-
@hook_event_name = 'TaskCompleted'
|
|
1400
|
-
end
|
|
1401
|
-
end
|
|
1402
|
-
|
|
1403
|
-
# ConfigChange hook input
|
|
1404
|
-
class ConfigChangeHookInput < BaseHookInput
|
|
1405
|
-
attr_accessor :source, :file_path
|
|
1406
|
-
|
|
1407
|
-
def initialize(attributes = {})
|
|
1408
|
-
super
|
|
1409
|
-
@hook_event_name = 'ConfigChange'
|
|
1410
|
-
end
|
|
1411
|
-
end
|
|
1412
|
-
|
|
1413
|
-
# WorktreeCreate hook input
|
|
1414
|
-
class WorktreeCreateHookInput < BaseHookInput
|
|
1415
|
-
attr_accessor :name
|
|
1416
|
-
|
|
1417
|
-
def initialize(attributes = {})
|
|
1418
|
-
super
|
|
1419
|
-
@hook_event_name = 'WorktreeCreate'
|
|
1420
|
-
end
|
|
1421
|
-
end
|
|
1422
|
-
|
|
1423
|
-
# WorktreeRemove hook input
|
|
1424
|
-
class WorktreeRemoveHookInput < BaseHookInput
|
|
1425
|
-
attr_accessor :worktree_path
|
|
1426
|
-
|
|
1427
|
-
def initialize(attributes = {})
|
|
1428
|
-
super
|
|
1429
|
-
@hook_event_name = 'WorktreeRemove'
|
|
1430
|
-
end
|
|
1431
|
-
end
|
|
1432
|
-
|
|
1433
|
-
# StopFailure hook input
|
|
1434
|
-
class StopFailureHookInput < BaseHookInput
|
|
1435
|
-
attr_accessor :error, :error_details, :last_assistant_message
|
|
1436
|
-
|
|
1437
|
-
def initialize(attributes = {})
|
|
1438
|
-
super
|
|
1439
|
-
@hook_event_name = 'StopFailure'
|
|
1440
|
-
end
|
|
1441
|
-
end
|
|
1442
|
-
|
|
1443
|
-
# PostCompact hook input
|
|
1444
|
-
class PostCompactHookInput < BaseHookInput
|
|
1445
|
-
attr_accessor :trigger, :compact_summary
|
|
1446
|
-
|
|
1447
|
-
def initialize(attributes = {})
|
|
1448
|
-
super
|
|
1449
|
-
@hook_event_name = 'PostCompact'
|
|
1450
|
-
end
|
|
1451
|
-
end
|
|
1452
|
-
|
|
1453
|
-
# PermissionDenied hook input
|
|
1454
|
-
class PermissionDeniedHookInput < BaseHookInput
|
|
1455
|
-
attr_accessor :tool_name, :tool_input, :tool_use_id, :reason, :agent_id, :agent_type
|
|
1456
|
-
|
|
1457
|
-
def initialize(attributes = {})
|
|
1458
|
-
super
|
|
1459
|
-
@hook_event_name = 'PermissionDenied'
|
|
1460
|
-
end
|
|
1461
|
-
end
|
|
1462
|
-
|
|
1463
|
-
# TaskCreated hook input
|
|
1464
|
-
class TaskCreatedHookInput < BaseHookInput
|
|
1465
|
-
attr_accessor :task_id, :task_subject, :task_description, :teammate_name, :team_name
|
|
1466
|
-
|
|
1467
|
-
def initialize(attributes = {})
|
|
1468
|
-
super
|
|
1469
|
-
@hook_event_name = 'TaskCreated'
|
|
1470
|
-
end
|
|
1471
|
-
end
|
|
1472
|
-
|
|
1473
|
-
# Elicitation hook input
|
|
1474
|
-
class ElicitationHookInput < BaseHookInput
|
|
1475
|
-
attr_accessor :mcp_server_name, :message, :mode, :url,
|
|
1476
|
-
:elicitation_id, :requested_schema
|
|
1477
|
-
|
|
1478
|
-
def initialize(attributes = {})
|
|
1479
|
-
super
|
|
1480
|
-
@hook_event_name = 'Elicitation'
|
|
1481
|
-
end
|
|
1482
|
-
end
|
|
1483
|
-
|
|
1484
|
-
# ElicitationResult hook input
|
|
1485
|
-
class ElicitationResultHookInput < BaseHookInput
|
|
1486
|
-
attr_accessor :mcp_server_name, :elicitation_id, :mode, :action, :content
|
|
1487
|
-
|
|
1488
|
-
def initialize(attributes = {})
|
|
1489
|
-
super
|
|
1490
|
-
@hook_event_name = 'ElicitationResult'
|
|
1491
|
-
end
|
|
1492
|
-
end
|
|
1493
|
-
|
|
1494
|
-
# InstructionsLoaded hook input
|
|
1495
|
-
class InstructionsLoadedHookInput < BaseHookInput
|
|
1496
|
-
attr_accessor :file_path, :memory_type, :load_reason, :globs, :trigger_file_path
|
|
1497
|
-
|
|
1498
|
-
def initialize(attributes = {})
|
|
1499
|
-
super
|
|
1500
|
-
@hook_event_name = 'InstructionsLoaded'
|
|
1501
|
-
end
|
|
1502
|
-
end
|
|
1503
|
-
|
|
1504
|
-
# CwdChanged hook input
|
|
1505
|
-
class CwdChangedHookInput < BaseHookInput
|
|
1506
|
-
attr_accessor :old_cwd, :new_cwd
|
|
1507
|
-
|
|
1508
|
-
def initialize(attributes = {})
|
|
1509
|
-
super
|
|
1510
|
-
@hook_event_name = 'CwdChanged'
|
|
1511
|
-
end
|
|
1512
|
-
end
|
|
1513
|
-
|
|
1514
|
-
# FileChanged hook input
|
|
1515
|
-
class FileChangedHookInput < BaseHookInput
|
|
1516
|
-
attr_accessor :file_path, :event
|
|
1517
|
-
|
|
1518
|
-
def initialize(attributes = {})
|
|
1519
|
-
super
|
|
1520
|
-
@hook_event_name = 'FileChanged'
|
|
1521
|
-
end
|
|
1522
|
-
end
|
|
1523
|
-
|
|
1524
|
-
# Fallback for hook events the SDK does not yet model. Carries the wire
|
|
1525
|
-
# event name and the complete raw payload so no fields are lost (Python
|
|
1526
|
-
# passes hook input through as a raw dict, so unknown events lose
|
|
1527
|
-
# nothing there).
|
|
1528
|
-
class UnknownHookInput < BaseHookInput
|
|
1529
|
-
attr_accessor :raw_input
|
|
1530
|
-
|
|
1531
|
-
def initialize(attributes = {})
|
|
1532
|
-
super
|
|
1533
|
-
# Direct assignment: BaseHookInput exposes hook_event_name as
|
|
1534
|
-
# attr_reader only, and Type#assign_attribute silently drops keys
|
|
1535
|
-
# without public setters.
|
|
1536
|
-
@hook_event_name = attributes[:hook_event_name] || attributes['hook_event_name']
|
|
1537
|
-
end
|
|
1538
|
-
end
|
|
1539
|
-
|
|
1540
|
-
# Setup hook specific output
|
|
1541
|
-
class SetupHookSpecificOutput < Type
|
|
1542
|
-
attr_accessor :additional_context
|
|
1543
|
-
attr_reader :hook_event_name
|
|
1544
|
-
|
|
1545
|
-
def initialize(attributes = {})
|
|
1546
|
-
super
|
|
1547
|
-
@hook_event_name = 'Setup'
|
|
1548
|
-
end
|
|
1549
|
-
|
|
1550
|
-
def to_h
|
|
1551
|
-
result = { hookEventName: @hook_event_name }
|
|
1552
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1553
|
-
result
|
|
1554
|
-
end
|
|
1555
|
-
end
|
|
1556
|
-
|
|
1557
|
-
# PreToolUse hook specific output
|
|
1558
|
-
class PreToolUseHookSpecificOutput < Type
|
|
1559
|
-
attr_accessor :permission_decision, :permission_decision_reason,
|
|
1560
|
-
:updated_input, :additional_context
|
|
1561
|
-
attr_reader :hook_event_name
|
|
1562
|
-
|
|
1563
|
-
def initialize(attributes = {})
|
|
1564
|
-
super
|
|
1565
|
-
@hook_event_name = 'PreToolUse'
|
|
1566
|
-
end
|
|
1567
|
-
|
|
1568
|
-
def to_h
|
|
1569
|
-
result = { hookEventName: @hook_event_name }
|
|
1570
|
-
result[:permissionDecision] = @permission_decision if @permission_decision
|
|
1571
|
-
result[:permissionDecisionReason] = @permission_decision_reason if @permission_decision_reason
|
|
1572
|
-
result[:updatedInput] = @updated_input if @updated_input
|
|
1573
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1574
|
-
result
|
|
1575
|
-
end
|
|
1576
|
-
end
|
|
1577
|
-
|
|
1578
|
-
# PostToolUse hook specific output.
|
|
1579
|
-
#
|
|
1580
|
-
# `updated_tool_output` (CLI 2.1.110+) replaces the tool's output entirely
|
|
1581
|
-
# — works for any tool, MCP or built-in. `updated_mcp_tool_output` is the
|
|
1582
|
-
# legacy MCP-only field that pre-dates the unified one; the CLI still
|
|
1583
|
-
# honors it, so both are emitted when set. Mirrors Python's
|
|
1584
|
-
# `PostToolUseHookSpecificOutput`.
|
|
1585
|
-
class PostToolUseHookSpecificOutput < Type
|
|
1586
|
-
attr_accessor :additional_context, :updated_mcp_tool_output, :updated_tool_output
|
|
1587
|
-
attr_reader :hook_event_name
|
|
1588
|
-
|
|
1589
|
-
def initialize(attributes = {})
|
|
1590
|
-
super
|
|
1591
|
-
@hook_event_name = 'PostToolUse'
|
|
1592
|
-
end
|
|
1593
|
-
|
|
1594
|
-
def to_h
|
|
1595
|
-
result = { hookEventName: @hook_event_name }
|
|
1596
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1597
|
-
result[:updatedToolOutput] = @updated_tool_output unless @updated_tool_output.nil?
|
|
1598
|
-
result[:updatedMCPToolOutput] = @updated_mcp_tool_output if @updated_mcp_tool_output
|
|
1599
|
-
result
|
|
1600
|
-
end
|
|
1601
|
-
end
|
|
1602
|
-
|
|
1603
|
-
# PostToolUseFailure hook specific output
|
|
1604
|
-
class PostToolUseFailureHookSpecificOutput < Type
|
|
1605
|
-
attr_accessor :additional_context
|
|
1606
|
-
attr_reader :hook_event_name
|
|
1607
|
-
|
|
1608
|
-
def initialize(attributes = {})
|
|
1609
|
-
super
|
|
1610
|
-
@hook_event_name = 'PostToolUseFailure'
|
|
1611
|
-
end
|
|
1612
|
-
|
|
1613
|
-
def to_h
|
|
1614
|
-
result = { hookEventName: @hook_event_name }
|
|
1615
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1616
|
-
result
|
|
1617
|
-
end
|
|
1618
|
-
end
|
|
1619
|
-
|
|
1620
|
-
# UserPromptSubmit hook specific output
|
|
1621
|
-
class UserPromptSubmitHookSpecificOutput < Type
|
|
1622
|
-
attr_accessor :additional_context
|
|
1623
|
-
attr_reader :hook_event_name
|
|
1624
|
-
|
|
1625
|
-
def initialize(attributes = {})
|
|
1626
|
-
super
|
|
1627
|
-
@hook_event_name = 'UserPromptSubmit'
|
|
1628
|
-
end
|
|
1629
|
-
|
|
1630
|
-
def to_h
|
|
1631
|
-
result = { hookEventName: @hook_event_name }
|
|
1632
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1633
|
-
result
|
|
1634
|
-
end
|
|
1635
|
-
end
|
|
1636
|
-
|
|
1637
|
-
# Notification hook specific output
|
|
1638
|
-
class NotificationHookSpecificOutput < Type
|
|
1639
|
-
attr_accessor :additional_context
|
|
1640
|
-
attr_reader :hook_event_name
|
|
1641
|
-
|
|
1642
|
-
def initialize(attributes = {})
|
|
1643
|
-
super
|
|
1644
|
-
@hook_event_name = 'Notification'
|
|
1645
|
-
end
|
|
1646
|
-
|
|
1647
|
-
def to_h
|
|
1648
|
-
result = { hookEventName: @hook_event_name }
|
|
1649
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1650
|
-
result
|
|
1651
|
-
end
|
|
1652
|
-
end
|
|
1653
|
-
|
|
1654
|
-
# SubagentStart hook specific output
|
|
1655
|
-
class SubagentStartHookSpecificOutput < Type
|
|
1656
|
-
attr_accessor :additional_context
|
|
1657
|
-
attr_reader :hook_event_name
|
|
1658
|
-
|
|
1659
|
-
def initialize(attributes = {})
|
|
1660
|
-
super
|
|
1661
|
-
@hook_event_name = 'SubagentStart'
|
|
1662
|
-
end
|
|
1663
|
-
|
|
1664
|
-
def to_h
|
|
1665
|
-
result = { hookEventName: @hook_event_name }
|
|
1666
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1667
|
-
result
|
|
1668
|
-
end
|
|
1669
|
-
end
|
|
1670
|
-
|
|
1671
|
-
# PermissionRequest hook specific output
|
|
1672
|
-
class PermissionRequestHookSpecificOutput < Type
|
|
1673
|
-
attr_accessor :decision
|
|
1674
|
-
attr_reader :hook_event_name
|
|
1675
|
-
|
|
1676
|
-
def initialize(attributes = {})
|
|
1677
|
-
super
|
|
1678
|
-
@hook_event_name = 'PermissionRequest'
|
|
1679
|
-
end
|
|
1680
|
-
|
|
1681
|
-
def to_h
|
|
1682
|
-
result = { hookEventName: @hook_event_name }
|
|
1683
|
-
result[:decision] = @decision if @decision
|
|
1684
|
-
result
|
|
1685
|
-
end
|
|
1686
|
-
end
|
|
1687
|
-
|
|
1688
|
-
# SessionStart hook specific output
|
|
1689
|
-
class SessionStartHookSpecificOutput < Type
|
|
1690
|
-
attr_accessor :additional_context
|
|
1691
|
-
attr_reader :hook_event_name
|
|
1692
|
-
|
|
1693
|
-
def initialize(attributes = {})
|
|
1694
|
-
super
|
|
1695
|
-
@hook_event_name = 'SessionStart'
|
|
1696
|
-
end
|
|
1697
|
-
|
|
1698
|
-
def to_h
|
|
1699
|
-
result = { hookEventName: @hook_event_name }
|
|
1700
|
-
result[:additionalContext] = @additional_context if @additional_context
|
|
1701
|
-
result
|
|
1702
|
-
end
|
|
1703
|
-
end
|
|
1704
|
-
|
|
1705
|
-
# PermissionDenied hook specific output
|
|
1706
|
-
class PermissionDeniedHookSpecificOutput < Type
|
|
1707
|
-
attr_accessor :retry
|
|
1708
|
-
attr_reader :hook_event_name
|
|
1709
|
-
|
|
1710
|
-
def initialize(attributes = {})
|
|
1711
|
-
super
|
|
1712
|
-
@hook_event_name = 'PermissionDenied'
|
|
1713
|
-
@retry = false if @retry.nil?
|
|
1714
|
-
end
|
|
1715
|
-
|
|
1716
|
-
def to_h
|
|
1717
|
-
result = { hookEventName: @hook_event_name }
|
|
1718
|
-
result[:retry] = @retry unless @retry.nil?
|
|
1719
|
-
result
|
|
1720
|
-
end
|
|
1721
|
-
end
|
|
1722
|
-
|
|
1723
|
-
# CwdChanged hook specific output
|
|
1724
|
-
class CwdChangedHookSpecificOutput < Type
|
|
1725
|
-
attr_accessor :watch_paths
|
|
1726
|
-
attr_reader :hook_event_name
|
|
1727
|
-
|
|
1728
|
-
def initialize(attributes = {})
|
|
1729
|
-
super
|
|
1730
|
-
@hook_event_name = 'CwdChanged'
|
|
1731
|
-
end
|
|
1732
|
-
|
|
1733
|
-
def to_h
|
|
1734
|
-
result = { hookEventName: @hook_event_name }
|
|
1735
|
-
result[:watchPaths] = @watch_paths if @watch_paths
|
|
1736
|
-
result
|
|
1737
|
-
end
|
|
1738
|
-
end
|
|
1739
|
-
|
|
1740
|
-
# FileChanged hook specific output
|
|
1741
|
-
class FileChangedHookSpecificOutput < Type
|
|
1742
|
-
attr_accessor :watch_paths
|
|
1743
|
-
attr_reader :hook_event_name
|
|
1744
|
-
|
|
1745
|
-
def initialize(attributes = {})
|
|
1746
|
-
super
|
|
1747
|
-
@hook_event_name = 'FileChanged'
|
|
1748
|
-
end
|
|
1749
|
-
|
|
1750
|
-
def to_h
|
|
1751
|
-
result = { hookEventName: @hook_event_name }
|
|
1752
|
-
result[:watchPaths] = @watch_paths if @watch_paths
|
|
1753
|
-
result
|
|
1754
|
-
end
|
|
1755
|
-
end
|
|
1756
|
-
|
|
1757
|
-
# Async hook JSON output
|
|
1758
|
-
class AsyncHookJSONOutput < Type
|
|
1759
|
-
attr_accessor :async, :async_timeout
|
|
1760
|
-
|
|
1761
|
-
def initialize(attributes = {})
|
|
1762
|
-
super
|
|
1763
|
-
@async = true if @async.nil?
|
|
1764
|
-
end
|
|
1765
|
-
|
|
1766
|
-
def to_h
|
|
1767
|
-
result = { async: @async }
|
|
1768
|
-
result[:asyncTimeout] = @async_timeout if @async_timeout
|
|
1769
|
-
result
|
|
1770
|
-
end
|
|
1771
|
-
end
|
|
1772
|
-
|
|
1773
|
-
# Sync hook JSON output
|
|
1774
|
-
class SyncHookJSONOutput < Type
|
|
1775
|
-
attr_accessor :continue, :suppress_output, :stop_reason, :decision,
|
|
1776
|
-
:system_message, :reason, :hook_specific_output
|
|
1777
|
-
|
|
1778
|
-
def initialize(attributes = {})
|
|
1779
|
-
super
|
|
1780
|
-
@continue = true if @continue.nil?
|
|
1781
|
-
@suppress_output = false if @suppress_output.nil?
|
|
1782
|
-
end
|
|
1783
|
-
|
|
1784
|
-
def to_h
|
|
1785
|
-
result = { continue: @continue }
|
|
1786
|
-
result[:suppressOutput] = @suppress_output if @suppress_output
|
|
1787
|
-
result[:stopReason] = @stop_reason if @stop_reason
|
|
1788
|
-
result[:decision] = @decision if @decision
|
|
1789
|
-
result[:systemMessage] = @system_message if @system_message
|
|
1790
|
-
result[:reason] = @reason if @reason
|
|
1791
|
-
result[:hookSpecificOutput] = @hook_specific_output.to_h if @hook_specific_output
|
|
1792
|
-
result
|
|
1793
|
-
end
|
|
1794
|
-
end
|
|
1795
|
-
|
|
1796
|
-
# MCP status response types
|
|
1797
|
-
|
|
1798
|
-
# MCP server connection status values
|
|
1799
|
-
MCP_SERVER_CONNECTION_STATUSES = %w[connected failed needs-auth pending disabled].freeze
|
|
1800
|
-
|
|
1801
|
-
# MCP server info (name and version)
|
|
1802
|
-
class McpServerInfo < Type
|
|
1803
|
-
attr_accessor :name, :version
|
|
1804
|
-
end
|
|
1805
|
-
|
|
1806
|
-
# MCP tool annotation hints
|
|
1807
|
-
class McpToolAnnotations < Type
|
|
1808
|
-
attr_accessor :read_only, :destructive, :open_world
|
|
1809
|
-
|
|
1810
|
-
# Backwards-compatible parse; returns nil for nil input.
|
|
1811
|
-
def self.parse(data)
|
|
1812
|
-
from_hash(data)
|
|
1813
|
-
end
|
|
1814
|
-
end
|
|
1815
|
-
|
|
1816
|
-
# MCP tool info (name, description, annotations)
|
|
1817
|
-
class McpToolInfo < Type
|
|
1818
|
-
attr_accessor :name, :description
|
|
1819
|
-
attr_reader :annotations
|
|
1820
|
-
|
|
1821
|
-
def annotations=(value)
|
|
1822
|
-
@annotations = value.is_a?(Hash) ? McpToolAnnotations.new(value) : value
|
|
1823
|
-
end
|
|
1824
|
-
|
|
1825
|
-
# Backwards-compatible parse; returns nil for nil input.
|
|
1826
|
-
def self.parse(data)
|
|
1827
|
-
from_hash(data)
|
|
1828
|
-
end
|
|
1829
|
-
end
|
|
1830
|
-
|
|
1831
|
-
# Output-only serializable version of McpSdkServerConfig (without live instance)
|
|
1832
|
-
# Returned in MCP status responses
|
|
1833
|
-
class McpSdkServerConfigStatus < Type
|
|
1834
|
-
attr_accessor :name
|
|
1835
|
-
attr_reader :type
|
|
1836
|
-
|
|
1837
|
-
def initialize(attributes = {})
|
|
1838
|
-
super
|
|
1839
|
-
@type = 'sdk'
|
|
1840
|
-
end
|
|
1841
|
-
|
|
1842
|
-
def to_h
|
|
1843
|
-
{ type: @type, name: @name }
|
|
1844
|
-
end
|
|
1845
|
-
end
|
|
1846
|
-
|
|
1847
|
-
# Claude.ai proxy MCP server config
|
|
1848
|
-
# Output-only type that appears in status responses for servers proxied through Claude.ai
|
|
1849
|
-
class McpClaudeAIProxyServerConfig < Type
|
|
1850
|
-
attr_accessor :url, :id
|
|
1851
|
-
attr_reader :type
|
|
1852
|
-
|
|
1853
|
-
def initialize(attributes = {})
|
|
1854
|
-
super
|
|
1855
|
-
@type = 'claudeai-proxy'
|
|
1856
|
-
end
|
|
1857
|
-
|
|
1858
|
-
def to_h
|
|
1859
|
-
{ type: @type, url: @url, id: @id }
|
|
1860
|
-
end
|
|
1861
|
-
end
|
|
1862
|
-
|
|
1863
|
-
# Status of a single MCP server connection
|
|
1864
|
-
class McpServerStatus < Type
|
|
1865
|
-
attr_accessor :name, :status, :error, :scope
|
|
1866
|
-
attr_reader :server_info, :config, :tools
|
|
1867
|
-
|
|
1868
|
-
def server_info=(value)
|
|
1869
|
-
@server_info = value.is_a?(Hash) ? McpServerInfo.new(value) : value
|
|
1870
|
-
end
|
|
1871
|
-
|
|
1872
|
-
def tools=(value)
|
|
1873
|
-
@tools = if value.is_a?(Array)
|
|
1874
|
-
value.map { |t| t.is_a?(Hash) ? McpToolInfo.new(t) : t }
|
|
1875
|
-
else
|
|
1876
|
-
value
|
|
1877
|
-
end
|
|
1878
|
-
end
|
|
1879
|
-
|
|
1880
|
-
def config=(value)
|
|
1881
|
-
@config = self.class.parse_config(value) || value
|
|
1882
|
-
end
|
|
1883
|
-
|
|
1884
|
-
# Backwards-compatible parse; normalizes camelCase `serverInfo` and
|
|
1885
|
-
# polymorphically builds the nested `config`.
|
|
1886
|
-
def self.parse(data)
|
|
1887
|
-
from_hash(data)
|
|
1888
|
-
end
|
|
1889
|
-
|
|
1890
|
-
def self.parse_config(config)
|
|
1891
|
-
return nil unless config.is_a?(Hash) && config[:type]
|
|
1892
|
-
|
|
1893
|
-
case config[:type]
|
|
1894
|
-
when 'claudeai-proxy'
|
|
1895
|
-
McpClaudeAIProxyServerConfig.new(url: config[:url], id: config[:id])
|
|
1896
|
-
when 'sdk'
|
|
1897
|
-
McpSdkServerConfigStatus.new(name: config[:name])
|
|
1898
|
-
else
|
|
1899
|
-
config
|
|
1900
|
-
end
|
|
1901
|
-
end
|
|
1902
|
-
end
|
|
1903
|
-
|
|
1904
|
-
# Response from get_mcp_status containing all server statuses
|
|
1905
|
-
class McpStatusResponse < Type
|
|
1906
|
-
attr_reader :mcp_servers
|
|
1907
|
-
|
|
1908
|
-
def mcp_servers=(value)
|
|
1909
|
-
@mcp_servers = if value.is_a?(Array)
|
|
1910
|
-
value.map { |s| s.is_a?(Hash) ? McpServerStatus.new(s) : s }
|
|
1911
|
-
else
|
|
1912
|
-
value
|
|
1913
|
-
end
|
|
1914
|
-
end
|
|
1915
|
-
|
|
1916
|
-
# Backwards-compatible parse; returns nil for nil input.
|
|
1917
|
-
def self.parse(data)
|
|
1918
|
-
from_hash(data)
|
|
1919
|
-
end
|
|
1920
|
-
end
|
|
1921
|
-
|
|
1922
|
-
# MCP Server configurations
|
|
1923
|
-
class McpStdioServerConfig < Type
|
|
1924
|
-
include Type::OptionValue
|
|
1925
|
-
|
|
1926
|
-
attr_accessor :command, :args, :env
|
|
1927
|
-
attr_reader :type
|
|
1928
|
-
|
|
1929
|
-
inspect_filtered :env
|
|
1930
|
-
|
|
1931
|
-
def initialize(attributes = {})
|
|
1932
|
-
super
|
|
1933
|
-
@type = 'stdio'
|
|
1934
|
-
end
|
|
1935
|
-
|
|
1936
|
-
def to_h
|
|
1937
|
-
result = { type: @type, command: @command }
|
|
1938
|
-
result[:args] = @args if @args
|
|
1939
|
-
result[:env] = @env if @env
|
|
1940
|
-
result
|
|
1941
|
-
end
|
|
1942
|
-
end
|
|
1943
|
-
|
|
1944
|
-
class McpSSEServerConfig < Type
|
|
1945
|
-
include Type::OptionValue
|
|
1946
|
-
|
|
1947
|
-
attr_accessor :url, :headers
|
|
1948
|
-
attr_reader :type
|
|
1949
|
-
|
|
1950
|
-
inspect_filtered :headers
|
|
1951
|
-
|
|
1952
|
-
def initialize(attributes = {})
|
|
1953
|
-
super
|
|
1954
|
-
@type = 'sse'
|
|
1955
|
-
end
|
|
1956
|
-
|
|
1957
|
-
def to_h
|
|
1958
|
-
result = { type: @type, url: @url }
|
|
1959
|
-
result[:headers] = @headers if @headers
|
|
1960
|
-
result
|
|
1961
|
-
end
|
|
1962
|
-
end
|
|
1963
|
-
|
|
1964
|
-
class McpHttpServerConfig < Type
|
|
1965
|
-
include Type::OptionValue
|
|
1966
|
-
|
|
1967
|
-
attr_accessor :url, :headers
|
|
1968
|
-
attr_reader :type
|
|
1969
|
-
|
|
1970
|
-
inspect_filtered :headers
|
|
1971
|
-
|
|
1972
|
-
def initialize(attributes = {})
|
|
1973
|
-
super
|
|
1974
|
-
@type = 'http'
|
|
1975
|
-
end
|
|
1976
|
-
|
|
1977
|
-
def to_h
|
|
1978
|
-
result = { type: @type, url: @url }
|
|
1979
|
-
result[:headers] = @headers if @headers
|
|
1980
|
-
result
|
|
1981
|
-
end
|
|
1982
|
-
end
|
|
1983
|
-
|
|
1984
|
-
class McpSdkServerConfig < Type
|
|
1985
|
-
include Type::OptionValue
|
|
1986
|
-
|
|
1987
|
-
attr_accessor :name, :instance
|
|
1988
|
-
attr_reader :type
|
|
1989
|
-
|
|
1990
|
-
def initialize(attributes = {})
|
|
1991
|
-
super
|
|
1992
|
-
@type = 'sdk'
|
|
1993
|
-
end
|
|
1994
|
-
|
|
1995
|
-
def to_h
|
|
1996
|
-
{ type: @type, name: @name, instance: @instance }
|
|
1997
|
-
end
|
|
1998
|
-
end
|
|
1999
|
-
|
|
2000
|
-
# SDK Plugin configuration
|
|
2001
|
-
class SdkPluginConfig < Type
|
|
2002
|
-
include Type::OptionValue
|
|
2003
|
-
|
|
2004
|
-
attr_accessor :path
|
|
2005
|
-
attr_reader :type
|
|
2006
|
-
|
|
2007
|
-
def initialize(attributes = {})
|
|
2008
|
-
super
|
|
2009
|
-
@type = 'local'
|
|
2010
|
-
end
|
|
2011
|
-
|
|
2012
|
-
def to_h
|
|
2013
|
-
{ type: @type, path: @path }
|
|
2014
|
-
end
|
|
2015
|
-
end
|
|
2016
|
-
|
|
2017
|
-
# Sandbox network configuration
|
|
2018
|
-
class SandboxNetworkConfig < Type
|
|
2019
|
-
include Type::OptionValue
|
|
2020
|
-
|
|
2021
|
-
attr_accessor :allowed_domains, :denied_domains, :allow_managed_domains_only,
|
|
2022
|
-
:allow_unix_sockets, :allow_all_unix_sockets, :allow_local_binding,
|
|
2023
|
-
:allow_mach_lookup, :http_proxy_port, :socks_proxy_port
|
|
2024
|
-
|
|
2025
|
-
def to_h
|
|
2026
|
-
result = {}
|
|
2027
|
-
result[:allowedDomains] = @allowed_domains if @allowed_domains
|
|
2028
|
-
result[:deniedDomains] = @denied_domains if @denied_domains
|
|
2029
|
-
result[:allowManagedDomainsOnly] = @allow_managed_domains_only unless @allow_managed_domains_only.nil?
|
|
2030
|
-
result[:allowUnixSockets] = @allow_unix_sockets unless @allow_unix_sockets.nil?
|
|
2031
|
-
result[:allowAllUnixSockets] = @allow_all_unix_sockets unless @allow_all_unix_sockets.nil?
|
|
2032
|
-
result[:allowLocalBinding] = @allow_local_binding unless @allow_local_binding.nil?
|
|
2033
|
-
result[:allowMachLookup] = @allow_mach_lookup if @allow_mach_lookup
|
|
2034
|
-
result[:httpProxyPort] = @http_proxy_port if @http_proxy_port
|
|
2035
|
-
result[:socksProxyPort] = @socks_proxy_port if @socks_proxy_port
|
|
2036
|
-
result
|
|
2037
|
-
end
|
|
2038
|
-
end
|
|
2039
|
-
|
|
2040
|
-
# Sandbox filesystem configuration
|
|
2041
|
-
class SandboxFilesystemConfig < Type
|
|
2042
|
-
include Type::OptionValue
|
|
2043
|
-
|
|
2044
|
-
attr_accessor :allow_write, :deny_write, :deny_read, :allow_read, :allow_managed_read_paths_only
|
|
2045
|
-
|
|
2046
|
-
def to_h
|
|
2047
|
-
result = {}
|
|
2048
|
-
result[:allowWrite] = @allow_write if @allow_write
|
|
2049
|
-
result[:denyWrite] = @deny_write if @deny_write
|
|
2050
|
-
result[:denyRead] = @deny_read if @deny_read
|
|
2051
|
-
result[:allowRead] = @allow_read if @allow_read
|
|
2052
|
-
result[:allowManagedReadPathsOnly] = @allow_managed_read_paths_only unless @allow_managed_read_paths_only.nil?
|
|
2053
|
-
result
|
|
2054
|
-
end
|
|
2055
|
-
end
|
|
2056
|
-
|
|
2057
|
-
# Sandbox settings for isolated command execution
|
|
2058
|
-
class SandboxSettings < Type
|
|
2059
|
-
include Type::OptionValue
|
|
2060
|
-
|
|
2061
|
-
attr_accessor :enabled, :fail_if_unavailable, :auto_allow_bash_if_sandboxed,
|
|
2062
|
-
:excluded_commands, :allow_unsandboxed_commands, :network, :filesystem,
|
|
2063
|
-
:ignore_violations, :enable_weaker_nested_sandbox,
|
|
2064
|
-
:enable_weaker_network_isolation, :ripgrep
|
|
2065
|
-
|
|
2066
|
-
def to_h
|
|
2067
|
-
result = {}
|
|
2068
|
-
result[:enabled] = @enabled unless @enabled.nil?
|
|
2069
|
-
result[:failIfUnavailable] = @fail_if_unavailable unless @fail_if_unavailable.nil?
|
|
2070
|
-
result[:autoAllowBashIfSandboxed] = @auto_allow_bash_if_sandboxed unless @auto_allow_bash_if_sandboxed.nil?
|
|
2071
|
-
result[:excludedCommands] = @excluded_commands if @excluded_commands
|
|
2072
|
-
result[:allowUnsandboxedCommands] = @allow_unsandboxed_commands unless @allow_unsandboxed_commands.nil?
|
|
2073
|
-
result[:network] = @network.is_a?(SandboxNetworkConfig) ? @network.to_h : @network if @network
|
|
2074
|
-
result[:filesystem] = @filesystem.is_a?(SandboxFilesystemConfig) ? @filesystem.to_h : @filesystem if @filesystem
|
|
2075
|
-
result[:ignoreViolations] = @ignore_violations if @ignore_violations
|
|
2076
|
-
result[:enableWeakerNestedSandbox] = @enable_weaker_nested_sandbox unless @enable_weaker_nested_sandbox.nil?
|
|
2077
|
-
result[:enableWeakerNetworkIsolation] = @enable_weaker_network_isolation unless @enable_weaker_network_isolation.nil?
|
|
2078
|
-
result[:ripgrep] = @ripgrep if @ripgrep
|
|
2079
|
-
result
|
|
2080
|
-
end
|
|
2081
|
-
end
|
|
2082
|
-
|
|
2083
|
-
# Result of a session fork operation
|
|
2084
|
-
class ForkSessionResult < Type
|
|
2085
|
-
attr_accessor :session_id
|
|
2086
|
-
end
|
|
2087
|
-
|
|
2088
|
-
# API-side task budget in tokens.
|
|
2089
|
-
# When set, the model is made aware of its remaining token budget so it can
|
|
2090
|
-
# pace tool use and wrap up before the limit.
|
|
2091
|
-
class TaskBudget < Type
|
|
2092
|
-
include Type::OptionValue
|
|
2093
|
-
|
|
2094
|
-
attr_accessor :total
|
|
2095
|
-
|
|
2096
|
-
def to_h
|
|
2097
|
-
{ total: @total }
|
|
2098
|
-
end
|
|
2099
|
-
end
|
|
2100
|
-
|
|
2101
|
-
# System prompt file configuration — loads system prompt from a file path
|
|
2102
|
-
class SystemPromptFile < Type
|
|
2103
|
-
include Type::OptionValue
|
|
2104
|
-
|
|
2105
|
-
attr_accessor :path
|
|
2106
|
-
attr_reader :type
|
|
2107
|
-
|
|
2108
|
-
def initialize(attributes = {})
|
|
2109
|
-
super
|
|
2110
|
-
@type = 'file'
|
|
2111
|
-
end
|
|
2112
|
-
|
|
2113
|
-
def to_h
|
|
2114
|
-
{ type: @type, path: @path }
|
|
2115
|
-
end
|
|
2116
|
-
end
|
|
2117
|
-
|
|
2118
|
-
# System prompt preset configuration.
|
|
2119
|
-
#
|
|
2120
|
-
# +snapshot+ controls whether the session keeps the system prompt it
|
|
2121
|
-
# recorded on its first request. When true, every later request (including
|
|
2122
|
-
# after resume) sends the recorded prompt, so a changed +append+ has no
|
|
2123
|
-
# effect until the session is compacted or a new session starts. When
|
|
2124
|
-
# false, the prompt is rebuilt on every request — useful while iterating on
|
|
2125
|
-
# +append+ text across calls that resume the same session. When nil
|
|
2126
|
-
# (omitted), the CLI treats it as true, except in bare mode (+--bare+),
|
|
2127
|
-
# where it acts as false. Sent on the control-protocol +initialize+ request
|
|
2128
|
-
# (never as a CLI flag); requires Claude Code CLI 2.1.257 or later, and
|
|
2129
|
-
# before 2.1.265 a session with an +append+ prompt recorded it only when
|
|
2130
|
-
# +snapshot+ was true. Older CLIs silently ignore it.
|
|
2131
|
-
class SystemPromptPreset < Type
|
|
2132
|
-
include Type::OptionValue
|
|
2133
|
-
|
|
2134
|
-
attr_reader :type
|
|
2135
|
-
attr_accessor :preset, :append, :exclude_dynamic_sections, :snapshot
|
|
2136
|
-
|
|
2137
|
-
def initialize(attributes = {})
|
|
2138
|
-
super
|
|
2139
|
-
@type = 'preset'
|
|
2140
|
-
end
|
|
2141
|
-
|
|
2142
|
-
def to_h
|
|
2143
|
-
result = { type: @type, preset: @preset }
|
|
2144
|
-
result[:append] = @append if @append
|
|
2145
|
-
result[:exclude_dynamic_sections] = @exclude_dynamic_sections unless @exclude_dynamic_sections.nil?
|
|
2146
|
-
result[:snapshot] = @snapshot unless @snapshot.nil?
|
|
2147
|
-
result
|
|
2148
|
-
end
|
|
2149
|
-
end
|
|
2150
|
-
|
|
2151
|
-
# Custom system prompt configuration — the object form of passing a String
|
|
2152
|
-
# as +system_prompt+. Reaches the CLI the same way a String does
|
|
2153
|
-
# (+--system-prompt <prompt>+); the object form exists so +snapshot+ can be
|
|
2154
|
-
# set alongside it (see SystemPromptPreset#snapshot for its semantics).
|
|
2155
|
-
class SystemPromptCustom < Type
|
|
2156
|
-
include Type::OptionValue
|
|
2157
|
-
|
|
2158
|
-
attr_reader :type
|
|
2159
|
-
attr_accessor :prompt, :snapshot
|
|
2160
|
-
|
|
2161
|
-
def initialize(attributes = {})
|
|
2162
|
-
super
|
|
2163
|
-
@type = 'custom'
|
|
2164
|
-
end
|
|
2165
|
-
|
|
2166
|
-
def to_h
|
|
2167
|
-
result = { type: @type, prompt: @prompt }
|
|
2168
|
-
result[:snapshot] = @snapshot unless @snapshot.nil?
|
|
2169
|
-
result
|
|
2170
|
-
end
|
|
2171
|
-
end
|
|
2172
|
-
|
|
2173
|
-
# Tools preset configuration
|
|
2174
|
-
class ToolsPreset < Type
|
|
2175
|
-
include Type::OptionValue
|
|
2176
|
-
|
|
2177
|
-
attr_reader :type
|
|
2178
|
-
attr_accessor :preset
|
|
2179
|
-
|
|
2180
|
-
def initialize(attributes = {})
|
|
2181
|
-
super
|
|
2182
|
-
@type = 'preset'
|
|
2183
|
-
end
|
|
2184
|
-
|
|
2185
|
-
def to_h
|
|
2186
|
-
{ type: @type, preset: @preset }
|
|
2187
|
-
end
|
|
2188
|
-
end
|
|
2189
|
-
|
|
2190
|
-
# Claude Agent Options for configuring queries
|
|
2191
|
-
class ClaudeAgentOptions < Type
|
|
2192
|
-
# `env` routinely carries credentials (ANTHROPIC_API_KEY, ...).
|
|
2193
|
-
inspect_filtered :env
|
|
2194
|
-
|
|
2195
|
-
attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
|
|
2196
|
-
:resume, :resume_session_at, :session_id, :max_turns, :disallowed_tools,
|
|
2197
|
-
:model, :permission_prompt_tool_name, :cwd, :cli_path, :settings,
|
|
2198
|
-
:add_dirs, :env, :extra_args, :max_buffer_size, :stderr,
|
|
2199
|
-
:can_use_tool, :hooks, :user,
|
|
2200
|
-
:agents, :setting_sources, :skills,
|
|
2201
|
-
:output_format, :max_budget_usd, :max_thinking_tokens,
|
|
2202
|
-
:fallback_model, :advisor_model, :plugins, :debug_stderr,
|
|
2203
|
-
:betas, :tools, :sandbox,
|
|
2204
|
-
:thinking, :effort, :observers, :task_budget,
|
|
2205
|
-
:session_store, :session_store_flush, :load_timeout_ms
|
|
2206
|
-
attr_reader :bare, :fork_session, :enable_file_checkpointing,
|
|
2207
|
-
:include_partial_messages, :continue_conversation,
|
|
2208
|
-
:include_hook_events, :strict_mcp_config,
|
|
2209
|
-
:callback_scheduling, :callback_wrapper
|
|
2210
|
-
|
|
2211
|
-
# With {#resume_session_at}: the UUID of the user prompt whose turn this
|
|
2212
|
-
# truncating resume intends to discard.
|
|
2213
|
-
#
|
|
2214
|
-
# When set, the CLI validates at load time that every transcript entry
|
|
2215
|
-
# after the `resume_session_at` point is attributable to that turn, and
|
|
2216
|
-
# refuses the resume otherwise — e.g. when the discarded range contains a
|
|
2217
|
-
# queued user message or task notification the session absorbed mid-turn
|
|
2218
|
-
# that the caller had not yet observed. Leave unset to keep the
|
|
2219
|
-
# unvalidated truncation behavior.
|
|
2220
|
-
#
|
|
2221
|
-
# **Choosing the fork point.** Set `resume_session_at` to the *last*
|
|
2222
|
-
# transcript entry of the turn you are keeping — whatever its type — and
|
|
2223
|
-
# `resume_drops_turn` to the prompt UUID of the turn immediately after it
|
|
2224
|
-
# (e.g. the next `SessionMessage` of `type == "user"` from
|
|
2225
|
-
# {ClaudeAgentSDK.get_session_messages}, or the `uuid` you supplied on a
|
|
2226
|
-
# streamed user message). Note that with structured output
|
|
2227
|
-
# ({#output_format}) or end-turn MCP tools a kept turn ends on entries
|
|
2228
|
-
# *after* its last assistant message, so forking at the assistant UUID is
|
|
2229
|
-
# refused by design.
|
|
2230
|
-
#
|
|
2231
|
-
# **On refusal.** The CLI reports an `error_during_execution` result whose
|
|
2232
|
-
# message starts with `Resume rejected by --resume-drops-turn:` — match on
|
|
2233
|
-
# that text. Treat it as deterministic: clear the pending fork target and
|
|
2234
|
-
# resume plainly rather than retrying the same request.
|
|
2235
|
-
#
|
|
2236
|
-
# Forwarded whenever it is not `nil`. An empty string reaches the CLI and
|
|
2237
|
-
# is rejected there as a malformed declaration rather than being dropped by
|
|
2238
|
-
# the SDK, which would silently disarm the guard you believe is armed. The
|
|
2239
|
-
# SDK does not validate the option combination (`resume` /
|
|
2240
|
-
# `resume_session_at`); like the TypeScript and Python SDKs that is the
|
|
2241
|
-
# CLI's call.
|
|
2242
|
-
#
|
|
2243
|
-
# @return [String, nil]
|
|
2244
|
-
# @see #resume_session_at
|
|
2245
|
-
attr_accessor :resume_drops_turn
|
|
2246
|
-
|
|
2247
|
-
def initialize(attributes = {})
|
|
2248
|
-
self.fork_session = false
|
|
2249
|
-
self.continue_conversation = false
|
|
2250
|
-
self.include_partial_messages = false
|
|
2251
|
-
self.enable_file_checkpointing = false
|
|
2252
|
-
self.include_hook_events = false
|
|
2253
|
-
self.strict_mcp_config = false
|
|
2254
|
-
self.forward_subagent_text = false
|
|
2255
|
-
|
|
2256
|
-
super(merge_with_defaults(attributes || {}))
|
|
2257
|
-
|
|
2258
|
-
# Non-nil defaults for options that need them.
|
|
2259
|
-
self.env ||= {}
|
|
2260
|
-
self.extra_args ||= {}
|
|
2261
|
-
self.mcp_servers ||= {}
|
|
2262
|
-
self.add_dirs ||= []
|
|
2263
|
-
self.observers ||= []
|
|
2264
|
-
self.allowed_tools ||= []
|
|
2265
|
-
self.disallowed_tools ||= []
|
|
2266
|
-
self.session_store_flush ||= 'batched'
|
|
2267
|
-
# 0 is a valid (immediate) timeout, so only fill in the default for nil.
|
|
2268
|
-
self.load_timeout_ms = 60_000 if load_timeout_ms.nil?
|
|
2269
|
-
self.callback_scheduling = :thread if callback_scheduling.nil?
|
|
2270
|
-
end
|
|
2271
|
-
|
|
2272
|
-
def dup_with(**changes)
|
|
2273
|
-
new_options = self.dup
|
|
2274
|
-
# A shallow #dup shares nested containers and typed option values, so
|
|
2275
|
-
# mutating a derived copy (e.g. `variant.allowed_tools << 'Bash'` or
|
|
2276
|
-
# `variant.sandbox.enabled = false`) would bleed into the base and every
|
|
2277
|
-
# sibling — including the security-relevant allow/deny lists and sandbox
|
|
2278
|
-
# rules. Deep-dup Hash/Array containers and option value types (Type#
|
|
2279
|
-
# dup_for_options, including those nested inside containers such as
|
|
2280
|
-
# agents[:x]); every other leaf (procs, SDK MCP server instances, store
|
|
2281
|
-
# adapters) keeps its identity.
|
|
2282
|
-
new_options.instance_variables.each do |ivar|
|
|
2283
|
-
new_options.instance_variable_set(ivar, Type.deep_dup_for_options(new_options.instance_variable_get(ivar)))
|
|
2284
|
-
end
|
|
2285
|
-
changes.each { |key, value| new_options[key] = value }
|
|
2286
|
-
new_options
|
|
2287
|
-
end
|
|
2288
|
-
|
|
2289
|
-
def bare?
|
|
2290
|
-
!!bare
|
|
2291
|
-
end
|
|
2292
|
-
|
|
2293
|
-
def bare=(value)
|
|
2294
|
-
@bare = coerce_boolean(value)
|
|
2295
|
-
end
|
|
2296
|
-
|
|
2297
|
-
def fork_session?
|
|
2298
|
-
!!fork_session
|
|
2299
|
-
end
|
|
2300
|
-
|
|
2301
|
-
def fork_session=(value)
|
|
2302
|
-
@fork_session = coerce_boolean(value)
|
|
2303
|
-
end
|
|
2304
|
-
|
|
2305
|
-
def enable_file_checkpointing?
|
|
2306
|
-
!!enable_file_checkpointing
|
|
2307
|
-
end
|
|
2308
|
-
|
|
2309
|
-
def enable_file_checkpointing=(value)
|
|
2310
|
-
@enable_file_checkpointing = coerce_boolean(value)
|
|
2311
|
-
end
|
|
2312
|
-
|
|
2313
|
-
def include_partial_messages?
|
|
2314
|
-
!!include_partial_messages
|
|
2315
|
-
end
|
|
2316
|
-
|
|
2317
|
-
def include_partial_messages=(value)
|
|
2318
|
-
@include_partial_messages = coerce_boolean(value)
|
|
2319
|
-
end
|
|
2320
|
-
|
|
2321
|
-
def continue_conversation?
|
|
2322
|
-
!!continue_conversation
|
|
2323
|
-
end
|
|
2324
|
-
|
|
2325
|
-
def continue_conversation=(value)
|
|
2326
|
-
@continue_conversation = coerce_boolean(value)
|
|
2327
|
-
end
|
|
2328
|
-
|
|
2329
|
-
def include_hook_events?
|
|
2330
|
-
!!include_hook_events
|
|
2331
|
-
end
|
|
2332
|
-
|
|
2333
|
-
def include_hook_events=(value)
|
|
2334
|
-
@include_hook_events = coerce_boolean(value)
|
|
2335
|
-
end
|
|
2336
|
-
|
|
2337
|
-
def strict_mcp_config?
|
|
2338
|
-
!!strict_mcp_config
|
|
2339
|
-
end
|
|
2340
|
-
|
|
2341
|
-
def strict_mcp_config=(value)
|
|
2342
|
-
@strict_mcp_config = coerce_boolean(value)
|
|
2343
|
-
end
|
|
2344
|
-
|
|
2345
|
-
# Forward subagent text and thinking blocks as messages in the stream.
|
|
2346
|
-
# Defaults to `false`.
|
|
2347
|
-
#
|
|
2348
|
-
# By default only `tool_use` / `tool_result` blocks from subagents
|
|
2349
|
-
# (spawned via the Agent tool) are emitted, as {AssistantMessage} /
|
|
2350
|
-
# {UserMessage} objects whose `parent_tool_use_id` is the spawning Agent
|
|
2351
|
-
# `tool_use` id — enough for a progress heartbeat. When true, the
|
|
2352
|
-
# subagent's text and thinking blocks are forwarded the same way, so
|
|
2353
|
-
# consumers can render the full nested transcript. Matches the TypeScript
|
|
2354
|
-
# SDK's `forwardSubagentText`.
|
|
2355
|
-
#
|
|
2356
|
-
# Sent as the `forwardSubagentText` initialize capability rather than a CLI
|
|
2357
|
-
# flag, and only when enabled, so an older CLI never sees an unknown key on
|
|
2358
|
-
# the common path. Both {ClaudeAgentSDK.query} and {Client} run the control
|
|
2359
|
-
# protocol, so the option applies to either entry point.
|
|
2360
|
-
#
|
|
2361
|
-
# Assigning coerces to a Boolean; {#forward_subagent_text?} is the
|
|
2362
|
-
# predicate form.
|
|
2363
|
-
#
|
|
2364
|
-
# @return [Boolean]
|
|
2365
|
-
attr_reader :forward_subagent_text
|
|
2366
|
-
|
|
2367
|
-
# @return [Boolean] {#forward_subagent_text}, as a strict Boolean.
|
|
2368
|
-
def forward_subagent_text?
|
|
2369
|
-
!!forward_subagent_text
|
|
2370
|
-
end
|
|
2371
|
-
|
|
2372
|
-
# @see #forward_subagent_text
|
|
2373
|
-
def forward_subagent_text=(value)
|
|
2374
|
-
@forward_subagent_text = coerce_boolean(value)
|
|
2375
|
-
end
|
|
2376
|
-
|
|
2377
|
-
# Request model-generated progress summaries for subagent (`local_agent`)
|
|
2378
|
-
# tasks. `true` *requests* generation: while the CLI has it enabled, a
|
|
2379
|
-
# subagent's {TaskProgressMessage#summary} **may** carry a one-line status.
|
|
2380
|
-
# `summary` stays optional on the wire even then — not every progress
|
|
2381
|
-
# frame has one — so read it nil-safely. `false` / `nil` do not enable
|
|
2382
|
-
# generation; they do not promise that `summary` is absent (a process that
|
|
2383
|
-
# already enabled summaries keeps them, and a backgrounded `mcp_task`
|
|
2384
|
-
# reports its own status there regardless of this option). Matches the
|
|
2385
|
-
# CLI's `agentProgressSummaries` initialize field.
|
|
2386
|
-
#
|
|
2387
|
-
# Defaults to `nil` (unset): the key is omitted from the `initialize`
|
|
2388
|
-
# control request. `true` and `false` are forwarded verbatim. This is an
|
|
2389
|
-
# enable switch, not a live toggle: CLI 2.1.278 only acts on a truthy
|
|
2390
|
-
# value, so `false` is schema-valid but equivalent to leaving the option
|
|
2391
|
-
# unset — it does not switch summaries off on a process that already
|
|
2392
|
-
# enabled them. Both {ClaudeAgentSDK.query} and {Client} run the control
|
|
2393
|
-
# protocol, so the option applies to either entry point.
|
|
2394
|
-
#
|
|
2395
|
-
# Assigning coerces to a Boolean and keeps `nil` as `nil`.
|
|
2396
|
-
#
|
|
2397
|
-
# @return [Boolean, nil]
|
|
2398
|
-
attr_reader :agent_progress_summaries
|
|
2399
|
-
|
|
2400
|
-
# @see #agent_progress_summaries
|
|
2401
|
-
def agent_progress_summaries=(value)
|
|
2402
|
-
@agent_progress_summaries = coerce_boolean(value)
|
|
2403
|
-
end
|
|
2404
|
-
|
|
2405
|
-
CALLBACK_SCHEDULING_MODES = %i[thread inline].freeze
|
|
2406
|
-
|
|
2407
|
-
# Where user callbacks (hooks, can_use_tool, SDK MCP handlers, message
|
|
2408
|
-
# blocks, observers) run when the SDK is hosted inside an Async reactor:
|
|
2409
|
-
# :thread (default) — each callback hops to a plain thread, so
|
|
2410
|
-
# thread-keyed libraries (ActiveRecord, pg, ...) behave as usual.
|
|
2411
|
-
# :inline — callbacks run in place on the reactor fiber. Only for
|
|
2412
|
-
# hosts that are fiber-isolated end to end (e.g. solid_queue fiber
|
|
2413
|
-
# workers with IsolatedExecutionState.isolation_level = :fiber).
|
|
2414
|
-
# Scheduler-opaque blocking (CPU-bound work, GVL-holding C
|
|
2415
|
-
# extensions) then stalls the whole reactor — wrap GVL-releasing
|
|
2416
|
-
# blocking and Ruby CPU work in ClaudeAgentSDK.offload { }; work
|
|
2417
|
-
# that holds the GVL throughout needs a subprocess.
|
|
2418
|
-
# Named after the mechanism, not a safety claim: whether inline is safe
|
|
2419
|
-
# depends on the host satisfying the fiber-isolation precondition.
|
|
2420
|
-
def callback_scheduling=(value)
|
|
2421
|
-
if value.nil?
|
|
2422
|
-
@callback_scheduling = nil
|
|
2423
|
-
return
|
|
2424
|
-
end
|
|
2425
|
-
|
|
2426
|
-
mode = value.respond_to?(:to_sym) ? value.to_sym : value
|
|
2427
|
-
unless CALLBACK_SCHEDULING_MODES.include?(mode)
|
|
2428
|
-
raise ArgumentError,
|
|
2429
|
-
"callback_scheduling must be one of #{CALLBACK_SCHEDULING_MODES.map(&:inspect).join(', ')} " \
|
|
2430
|
-
"(got #{value.inspect})"
|
|
2431
|
-
end
|
|
2432
|
-
|
|
2433
|
-
@callback_scheduling = mode
|
|
2434
|
-
end
|
|
2435
|
-
|
|
2436
|
-
# Middleware wrapped around EVERY user-callback dispatch (message
|
|
2437
|
-
# blocks, observers, hooks, permission callbacks, SDK MCP handlers).
|
|
2438
|
-
# A callable receiving a zero-arg invocation; it MUST call it and
|
|
2439
|
-
# return its value:
|
|
2440
|
-
#
|
|
2441
|
-
# callback_wrapper: ->(invocation) { MyApm.trace('agent.callback') { invocation.call } }
|
|
2442
|
-
#
|
|
2443
|
-
# The wrapper runs on the same execution context as the callback —
|
|
2444
|
-
# inside the worker thread in :thread mode, in place on the reactor
|
|
2445
|
-
# fiber in :inline mode. Exceptions propagate through it unchanged; it
|
|
2446
|
-
# must not swallow them. Default nil (no wrapping).
|
|
2447
|
-
#
|
|
2448
|
-
# Rails apps: use ClaudeAgentSDK::Railtie.callback_wrapper, which runs
|
|
2449
|
-
# callbacks in the Rails executor (AR connections check back in when the
|
|
2450
|
-
# callback ends). A bare `Rails.application.executor.wrap` deadlocks
|
|
2451
|
-
# under development code reloading in :thread mode.
|
|
2452
|
-
def callback_wrapper=(value)
|
|
2453
|
-
raise ArgumentError, "callback_wrapper must be a callable or nil (got #{value.inspect})" unless value.nil? || value.respond_to?(:call)
|
|
2454
|
-
|
|
2455
|
-
@callback_wrapper = value
|
|
2456
|
-
end
|
|
2457
|
-
|
|
2458
|
-
private
|
|
2459
|
-
|
|
2460
|
-
# Strict key validation: unlike other Type subclasses (which silently drop
|
|
2461
|
-
# unknown keys for forward-compat with newer CLI output), ClaudeAgentOptions
|
|
2462
|
-
# is a developer-facing config object — typos should fail loudly.
|
|
2463
|
-
def assign_attribute(name, value)
|
|
2464
|
-
setter = :"#{normalize_name(name)}="
|
|
2465
|
-
raise ArgumentError, "unknown ClaudeAgentOptions option: #{name.inspect}" unless respond_to?(setter)
|
|
2466
|
-
|
|
2467
|
-
public_send(setter, value)
|
|
2468
|
-
end
|
|
2469
|
-
|
|
2470
|
-
# Merge caller-provided attributes with configured defaults.
|
|
2471
|
-
# Only keys the caller explicitly passed are treated as overrides;
|
|
2472
|
-
# method-signature defaults ([], {}, false) are NOT present unless the caller wrote them.
|
|
2473
|
-
#
|
|
2474
|
-
# Both sides are keyed by the option they name, not by their literal
|
|
2475
|
-
# spelling: Type accepts symbol/string and snake_case/camelCase names, so a
|
|
2476
|
-
# caller's `'permissionMode' => nil` must still inherit a configured
|
|
2477
|
-
# `permission_mode:` (and a Hash must still merge into it) rather than
|
|
2478
|
-
# riding along as a second entry that overwrites the default on assignment.
|
|
2479
|
-
def merge_with_defaults(attributes)
|
|
2480
|
-
return attributes unless defined?(ClaudeAgentSDK) && ClaudeAgentSDK.respond_to?(:default_options)
|
|
2481
|
-
|
|
2482
|
-
defaults = ClaudeAgentSDK.default_options
|
|
2483
|
-
return attributes unless defaults.any?
|
|
2484
|
-
|
|
2485
|
-
# Start from configured defaults. Container values, typed option
|
|
2486
|
-
# values (SandboxSettings, SystemPromptPreset, AgentDefinition, ...)
|
|
2487
|
-
# and mutable Strings are recursively copied (Type.deep_dup_for_options)
|
|
2488
|
-
# so per-instance mutation (options.allowed_tools << 'Bash',
|
|
2489
|
-
# options.sandbox.enabled = false) can never corrupt the global
|
|
2490
|
-
# defaults or reach another session; other leaves (frozen Strings,
|
|
2491
|
-
# Procs, SdkMcpServer instances, store adapters) intentionally keep
|
|
2492
|
-
# identity. The stored defaults are a frozen snapshot
|
|
2493
|
-
# (Configuration#default_options=), and the copy is what makes each
|
|
2494
|
-
# session's containers and values mutable again — its Strings stay the
|
|
2495
|
-
# snapshot's frozen ones, so `options.model << 'x'` fails loudly rather
|
|
2496
|
-
# than reaching other sessions; reassign instead.
|
|
2497
|
-
result = {}
|
|
2498
|
-
defaults.each { |key, value| result[option_key(key)] = Type.deep_dup_for_options(value) }
|
|
2499
|
-
attributes.each do |key, value|
|
|
2500
|
-
key = option_key(key)
|
|
2501
|
-
default_val = result[key]
|
|
2502
|
-
result[key] = if value.nil?
|
|
2503
|
-
default_val # nil means "no preference" — keep the configured default
|
|
2504
|
-
elsif default_val.is_a?(Hash) && value.is_a?(Hash)
|
|
2505
|
-
default_val.merge(value)
|
|
2506
|
-
else
|
|
2507
|
-
value
|
|
2508
|
-
end
|
|
2509
|
-
end
|
|
2510
|
-
result
|
|
2511
|
-
end
|
|
2512
|
-
|
|
2513
|
-
# The canonical Symbol for a known option, whatever its spelling. An
|
|
2514
|
-
# unknown name is returned untouched so assign_attribute's strict check
|
|
2515
|
-
# reports the typo exactly as the developer wrote it.
|
|
2516
|
-
def option_key(name)
|
|
2517
|
-
normalized = normalize_name(name)
|
|
2518
|
-
respond_to?(:"#{normalized}=") ? normalized.to_sym : name
|
|
2519
|
-
end
|
|
2520
|
-
end
|
|
2521
|
-
|
|
2522
|
-
# SDK MCP Tool definition
|
|
2523
|
-
class SdkMcpTool < Type
|
|
2524
|
-
attr_accessor :name, :description, :input_schema, :handler, :annotations, :meta
|
|
2525
|
-
end
|
|
2526
|
-
|
|
2527
|
-
# SDK MCP Resource definition
|
|
2528
|
-
class SdkMcpResource < Type
|
|
2529
|
-
attr_accessor :uri, :name, :description, :mime_type, :reader
|
|
2530
|
-
end
|
|
2531
|
-
|
|
2532
|
-
# SDK MCP Prompt definition
|
|
2533
|
-
class SdkMcpPrompt < Type
|
|
2534
|
-
attr_accessor :name, :description, :arguments, :generator
|
|
2535
|
-
end
|
|
2536
|
-
end
|
|
3
|
+
# Entry point for the SDK's value types. The definitions live in
|
|
4
|
+
# lib/claude_agent_sdk/types/, one file per area; this file loads all of them,
|
|
5
|
+
# so `require 'claude_agent_sdk/types'` still defines every type. `base` (the
|
|
6
|
+
# Type superclass) comes first; each part also requires it itself.
|
|
7
|
+
require_relative 'types/base'
|
|
8
|
+
require_relative 'types/content_blocks'
|
|
9
|
+
require_relative 'types/messages'
|
|
10
|
+
require_relative 'types/option_values'
|
|
11
|
+
require_relative 'types/permissions'
|
|
12
|
+
require_relative 'types/hooks'
|
|
13
|
+
require_relative 'types/mcp'
|
|
14
|
+
require_relative 'types/sessions'
|
|
15
|
+
require_relative 'types/options'
|