claude-agent-sdk 0.16.4 → 0.16.6
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 +21 -0
- data/README.md +1 -1
- data/lib/claude_agent_sdk/command_builder.rb +11 -0
- data/lib/claude_agent_sdk/message_parser.rb +29 -186
- data/lib/claude_agent_sdk/query.rb +14 -11
- data/lib/claude_agent_sdk/types.rb +602 -952
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/claude_agent_sdk.rb +10 -4
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5e77eea013fead986f47ff000864ddb9e52a3408186cf09bf3bb8d5214edec28
|
|
4
|
+
data.tar.gz: 49f94edca00072ec403cb26eb79165979d8684e655336bce6ca53f3c1a31eb81
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '08544dd09bb2d8c1127364517e1cc3527aaa0321d8c2d957550f078c5ce1d20796f5622f987ddd9f128377cad697d314a63a31031cd8eeb2e823bbfef282ee17'
|
|
7
|
+
data.tar.gz: e7149a4d3d9546631449d71878a6480d3d8758c2d990b3836cdc1dde65e93d09a8d19658c781c4f2cf5655bcb3c910dac84bc7d0076f635af4957f78a2e91fe9
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.16.6] - 2026-04-29
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Type classes now accept hash construction with mixed key shapes — symbols or strings, snake_case or camelCase — and support bracket access (`obj[:field]`, `obj['field']`). `UserMessage.new({"sessionId" => "abc"})` works the same as `UserMessage.new(session_id: "abc")`. `Type.from_hash(nil)` and `Type.wrap(nil)` are nil-safe.
|
|
14
|
+
- `ClaudeAgentOptions.new(nil)` is accepted and yields an options object populated with configured defaults.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Discriminator fields on type classes (`type` on `SystemPromptFile`/`McpStdioServerConfig`/etc., `behavior` on `PermissionResultAllow`/`Deny`, `hook_event_name` on every hook input/output) are now `attr_reader`-only, set authoritatively in `initialize`. They cannot be overwritten externally.
|
|
18
|
+
- `ClaudeAgentOptions` continues to raise `ArgumentError` on unknown keys (constructor, `dup_with`, and `[]=`); other type subclasses silently drop unknown keys for forward-compatibility with newer CLI output.
|
|
19
|
+
|
|
20
|
+
### Internal
|
|
21
|
+
- Unified ~50 type classes (messages, hook inputs/outputs, MCP configs, system prompt configs, sandbox settings) under a shared `Type` base class. `lib/claude_agent_sdk/types.rb` shrank from 1510 to 588 lines; `MessageParser`'s system-message dispatch went from 215 lines to a 14-entry lookup table.
|
|
22
|
+
|
|
23
|
+
## [0.16.5] - 2026-04-24
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- `display:` option on `ThinkingConfigAdaptive` and `ThinkingConfigEnabled`, forwarded to the CLI as `--thinking-display <summarized|omitted>`. Opus 4.7 defaults thinking display to `"omitted"` (empty `thinking` field, signature only), so pass `ThinkingConfigAdaptive.new(display: "summarized")` to receive plaintext summarized thinking text. Invalid values raise `ArgumentError` at construction. See [adaptive thinking docs](https://docs.claude.com/en/docs/build-with-claude/adaptive-thinking).
|
|
27
|
+
|
|
28
|
+
### Internal
|
|
29
|
+
- Extracted private `writeln`/`write` helpers in `Client` and `Query` to consolidate the `@transport.write(json + "\n")` pattern across five call sites. Pure refactor; same bytes on the wire.
|
|
30
|
+
|
|
10
31
|
## [0.16.4] - 2026-04-23
|
|
11
32
|
|
|
12
33
|
### Fixed
|
data/README.md
CHANGED
|
@@ -108,7 +108,7 @@ Add this line to your application's Gemfile:
|
|
|
108
108
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
109
109
|
|
|
110
110
|
# Or use a stable version from RubyGems
|
|
111
|
-
gem 'claude-agent-sdk', '~> 0.16.
|
|
111
|
+
gem 'claude-agent-sdk', '~> 0.16.6'
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
And then execute:
|
|
@@ -158,8 +158,10 @@ module ClaudeAgentSDK
|
|
|
158
158
|
case @options.thinking
|
|
159
159
|
when ThinkingConfigAdaptive
|
|
160
160
|
cmd.push("--thinking", "adaptive")
|
|
161
|
+
append_thinking_display(cmd, @options.thinking.display)
|
|
161
162
|
when ThinkingConfigEnabled
|
|
162
163
|
cmd.push("--max-thinking-tokens", @options.thinking.budget_tokens.to_s)
|
|
164
|
+
append_thinking_display(cmd, @options.thinking.display)
|
|
163
165
|
when ThinkingConfigDisabled
|
|
164
166
|
cmd.push("--thinking", "disabled")
|
|
165
167
|
end
|
|
@@ -168,6 +170,15 @@ module ClaudeAgentSDK
|
|
|
168
170
|
end
|
|
169
171
|
end
|
|
170
172
|
|
|
173
|
+
# `--thinking-display` toggles between `"summarized"` (visible thinking
|
|
174
|
+
# text) and `"omitted"` (empty thinking, signature only). Opus 4.7 defaults
|
|
175
|
+
# to `"omitted"`, so pass `display: "summarized"` to see reasoning.
|
|
176
|
+
def append_thinking_display(cmd, display)
|
|
177
|
+
return if display.nil?
|
|
178
|
+
|
|
179
|
+
cmd.push("--thinking-display", display.to_s)
|
|
180
|
+
end
|
|
181
|
+
|
|
171
182
|
# The set of supported levels is model-dependent; the CLI falls back to
|
|
172
183
|
# the highest supported level at or below the one requested
|
|
173
184
|
# (e.g. `xhigh` → `high` on Opus 4.6).
|
|
@@ -78,214 +78,57 @@ module ClaudeAgentSDK
|
|
|
78
78
|
)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
# Typed SystemMessage subclasses inherit from `Type` and accept the raw
|
|
82
|
+
# CLI hash directly — camelCase and snake_case keys are normalized by the
|
|
83
|
+
# base class, and the full hash is captured as `#data`.
|
|
84
|
+
SYSTEM_MESSAGE_CLASSES = {
|
|
85
|
+
'init' => InitMessage,
|
|
86
|
+
'compact_boundary' => CompactBoundaryMessage,
|
|
87
|
+
'status' => StatusMessage,
|
|
88
|
+
'api_retry' => APIRetryMessage,
|
|
89
|
+
'local_command_output' => LocalCommandOutputMessage,
|
|
90
|
+
'hook_started' => HookStartedMessage,
|
|
91
|
+
'hook_progress' => HookProgressMessage,
|
|
92
|
+
'hook_response' => HookResponseMessage,
|
|
93
|
+
'session_state_changed' => SessionStateChangedMessage,
|
|
94
|
+
'files_persisted' => FilesPersistedMessage,
|
|
95
|
+
'elicitation_complete' => ElicitationCompleteMessage,
|
|
96
|
+
'task_started' => TaskStartedMessage,
|
|
97
|
+
'task_progress' => TaskProgressMessage,
|
|
98
|
+
'task_notification' => TaskNotificationMessage
|
|
99
|
+
}.freeze
|
|
100
|
+
|
|
81
101
|
def self.parse_system_message(data)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
InitMessage.new(
|
|
85
|
-
subtype: data[:subtype], data: data,
|
|
86
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
87
|
-
agents: data[:agents], api_key_source: data[:apiKeySource] || data[:api_key_source],
|
|
88
|
-
betas: data[:betas], claude_code_version: data[:claude_code_version],
|
|
89
|
-
cwd: data[:cwd], tools: data[:tools], mcp_servers: data[:mcp_servers],
|
|
90
|
-
model: data[:model], permission_mode: data[:permissionMode] || data[:permission_mode],
|
|
91
|
-
slash_commands: data[:slash_commands], output_style: data[:output_style],
|
|
92
|
-
skills: data[:skills], plugins: data[:plugins],
|
|
93
|
-
fast_mode_state: data[:fastModeState] || data[:fast_mode_state]
|
|
94
|
-
)
|
|
95
|
-
when 'compact_boundary'
|
|
96
|
-
raw_metadata = data[:compact_metadata]
|
|
97
|
-
CompactBoundaryMessage.new(
|
|
98
|
-
subtype: data[:subtype], data: data,
|
|
99
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
100
|
-
compact_metadata: CompactMetadata.from_hash(raw_metadata)
|
|
101
|
-
)
|
|
102
|
-
when 'status'
|
|
103
|
-
StatusMessage.new(
|
|
104
|
-
subtype: data[:subtype], data: data,
|
|
105
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
106
|
-
status: data[:status],
|
|
107
|
-
permission_mode: data[:permissionMode] || data[:permission_mode]
|
|
108
|
-
)
|
|
109
|
-
when 'api_retry'
|
|
110
|
-
APIRetryMessage.new(
|
|
111
|
-
subtype: data[:subtype], data: data,
|
|
112
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
113
|
-
attempt: data[:attempt], max_retries: data[:maxRetries] || data[:max_retries],
|
|
114
|
-
retry_delay_ms: data[:retryDelayMs] || data[:retry_delay_ms],
|
|
115
|
-
error_status: data[:errorStatus] || data[:error_status],
|
|
116
|
-
error: data[:error]
|
|
117
|
-
)
|
|
118
|
-
when 'local_command_output'
|
|
119
|
-
LocalCommandOutputMessage.new(
|
|
120
|
-
subtype: data[:subtype], data: data,
|
|
121
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
122
|
-
content: data[:content]
|
|
123
|
-
)
|
|
124
|
-
when 'hook_started'
|
|
125
|
-
HookStartedMessage.new(
|
|
126
|
-
subtype: data[:subtype], data: data,
|
|
127
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
128
|
-
hook_id: data[:hookId] || data[:hook_id],
|
|
129
|
-
hook_name: data[:hookName] || data[:hook_name],
|
|
130
|
-
hook_event: data[:hookEvent] || data[:hook_event]
|
|
131
|
-
)
|
|
132
|
-
when 'hook_progress'
|
|
133
|
-
HookProgressMessage.new(
|
|
134
|
-
subtype: data[:subtype], data: data,
|
|
135
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
136
|
-
hook_id: data[:hookId] || data[:hook_id],
|
|
137
|
-
hook_name: data[:hookName] || data[:hook_name],
|
|
138
|
-
hook_event: data[:hookEvent] || data[:hook_event],
|
|
139
|
-
stdout: data[:stdout], stderr: data[:stderr], output: data[:output]
|
|
140
|
-
)
|
|
141
|
-
when 'hook_response'
|
|
142
|
-
HookResponseMessage.new(
|
|
143
|
-
subtype: data[:subtype], data: data,
|
|
144
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
145
|
-
hook_id: data[:hookId] || data[:hook_id],
|
|
146
|
-
hook_name: data[:hookName] || data[:hook_name],
|
|
147
|
-
hook_event: data[:hookEvent] || data[:hook_event],
|
|
148
|
-
output: data[:output], stdout: data[:stdout], stderr: data[:stderr],
|
|
149
|
-
exit_code: data[:exitCode] || data[:exit_code],
|
|
150
|
-
outcome: data[:outcome]
|
|
151
|
-
)
|
|
152
|
-
when 'session_state_changed'
|
|
153
|
-
SessionStateChangedMessage.new(
|
|
154
|
-
subtype: data[:subtype], data: data,
|
|
155
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
156
|
-
state: data[:state]
|
|
157
|
-
)
|
|
158
|
-
when 'files_persisted'
|
|
159
|
-
FilesPersistedMessage.new(
|
|
160
|
-
subtype: data[:subtype], data: data,
|
|
161
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
162
|
-
files: data[:files], failed: data[:failed],
|
|
163
|
-
processed_at: data[:processedAt] || data[:processed_at]
|
|
164
|
-
)
|
|
165
|
-
when 'elicitation_complete'
|
|
166
|
-
ElicitationCompleteMessage.new(
|
|
167
|
-
subtype: data[:subtype], data: data,
|
|
168
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
169
|
-
mcp_server_name: data[:mcpServerName] || data[:mcp_server_name],
|
|
170
|
-
elicitation_id: data[:elicitationId] || data[:elicitation_id]
|
|
171
|
-
)
|
|
172
|
-
when 'task_started'
|
|
173
|
-
TaskStartedMessage.new(
|
|
174
|
-
subtype: data[:subtype], data: data,
|
|
175
|
-
task_id: data[:task_id], description: data[:description],
|
|
176
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
177
|
-
tool_use_id: data[:tool_use_id], task_type: data[:task_type],
|
|
178
|
-
workflow_name: data[:workflowName] || data[:workflow_name],
|
|
179
|
-
prompt: data[:prompt]
|
|
180
|
-
)
|
|
181
|
-
when 'task_progress'
|
|
182
|
-
TaskProgressMessage.new(
|
|
183
|
-
subtype: data[:subtype], data: data,
|
|
184
|
-
task_id: data[:task_id], description: data[:description],
|
|
185
|
-
usage: data[:usage], uuid: data[:uuid], session_id: data[:session_id],
|
|
186
|
-
tool_use_id: data[:tool_use_id], last_tool_name: data[:last_tool_name],
|
|
187
|
-
summary: data[:summary]
|
|
188
|
-
)
|
|
189
|
-
when 'task_notification'
|
|
190
|
-
TaskNotificationMessage.new(
|
|
191
|
-
subtype: data[:subtype], data: data,
|
|
192
|
-
task_id: data[:task_id], status: data[:status],
|
|
193
|
-
output_file: data[:output_file], summary: data[:summary],
|
|
194
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
195
|
-
tool_use_id: data[:tool_use_id], usage: data[:usage]
|
|
196
|
-
)
|
|
197
|
-
else
|
|
198
|
-
SystemMessage.new(subtype: data[:subtype], data: data)
|
|
199
|
-
end
|
|
102
|
+
klass = SYSTEM_MESSAGE_CLASSES[data[:subtype]] || SystemMessage
|
|
103
|
+
klass.new(data)
|
|
200
104
|
end
|
|
201
105
|
|
|
202
106
|
def self.parse_result_message(data)
|
|
203
|
-
ResultMessage.new(
|
|
204
|
-
subtype: data[:subtype],
|
|
205
|
-
duration_ms: data[:duration_ms],
|
|
206
|
-
duration_api_ms: data[:duration_api_ms],
|
|
207
|
-
is_error: data[:is_error],
|
|
208
|
-
num_turns: data[:num_turns],
|
|
209
|
-
session_id: data[:session_id],
|
|
210
|
-
stop_reason: data[:stop_reason],
|
|
211
|
-
total_cost_usd: data[:total_cost_usd],
|
|
212
|
-
usage: data[:usage],
|
|
213
|
-
result: data[:result],
|
|
214
|
-
structured_output: data[:structured_output],
|
|
215
|
-
model_usage: data[:modelUsage] || data[:model_usage],
|
|
216
|
-
permission_denials: data[:permission_denials],
|
|
217
|
-
errors: data[:errors],
|
|
218
|
-
uuid: data[:uuid],
|
|
219
|
-
fast_mode_state: data[:fastModeState] || data[:fast_mode_state]
|
|
220
|
-
)
|
|
107
|
+
ResultMessage.new(data)
|
|
221
108
|
end
|
|
222
109
|
|
|
223
110
|
def self.parse_stream_event(data)
|
|
224
|
-
StreamEvent.new(
|
|
225
|
-
uuid: data[:uuid],
|
|
226
|
-
session_id: data[:session_id],
|
|
227
|
-
event: data[:event],
|
|
228
|
-
parent_tool_use_id: data[:parent_tool_use_id]
|
|
229
|
-
)
|
|
111
|
+
StreamEvent.new(data)
|
|
230
112
|
end
|
|
231
113
|
|
|
232
114
|
def self.parse_rate_limit_event(data)
|
|
233
|
-
|
|
234
|
-
rate_limit_info = RateLimitInfo.new(
|
|
235
|
-
status: info[:status],
|
|
236
|
-
resets_at: info[:resetsAt],
|
|
237
|
-
rate_limit_type: info[:rateLimitType],
|
|
238
|
-
utilization: info[:utilization],
|
|
239
|
-
overage_status: info[:overageStatus],
|
|
240
|
-
overage_resets_at: info[:overageResetsAt],
|
|
241
|
-
overage_disabled_reason: info[:overageDisabledReason],
|
|
242
|
-
raw: info
|
|
243
|
-
)
|
|
244
|
-
RateLimitEvent.new(
|
|
245
|
-
rate_limit_info: rate_limit_info,
|
|
246
|
-
uuid: data[:uuid],
|
|
247
|
-
session_id: data[:session_id],
|
|
248
|
-
raw_data: data
|
|
249
|
-
)
|
|
115
|
+
RateLimitEvent.new(data.merge(raw_data: data))
|
|
250
116
|
end
|
|
251
117
|
|
|
252
118
|
def self.parse_tool_progress_message(data)
|
|
253
|
-
ToolProgressMessage.new(
|
|
254
|
-
uuid: data[:uuid],
|
|
255
|
-
session_id: data[:session_id],
|
|
256
|
-
tool_use_id: data[:toolUseId] || data[:tool_use_id],
|
|
257
|
-
tool_name: data[:toolName] || data[:tool_name],
|
|
258
|
-
parent_tool_use_id: data[:parentToolUseId] || data[:parent_tool_use_id],
|
|
259
|
-
elapsed_time_seconds: data[:elapsedTimeSeconds] || data[:elapsed_time_seconds],
|
|
260
|
-
task_id: data[:taskId] || data[:task_id]
|
|
261
|
-
)
|
|
119
|
+
ToolProgressMessage.new(data)
|
|
262
120
|
end
|
|
263
121
|
|
|
264
122
|
def self.parse_auth_status_message(data)
|
|
265
|
-
AuthStatusMessage.new(
|
|
266
|
-
uuid: data[:uuid],
|
|
267
|
-
session_id: data[:session_id],
|
|
268
|
-
is_authenticating: data[:isAuthenticating] || data[:is_authenticating],
|
|
269
|
-
output: data[:output],
|
|
270
|
-
error: data[:error]
|
|
271
|
-
)
|
|
123
|
+
AuthStatusMessage.new(data)
|
|
272
124
|
end
|
|
273
125
|
|
|
274
126
|
def self.parse_tool_use_summary_message(data)
|
|
275
|
-
ToolUseSummaryMessage.new(
|
|
276
|
-
uuid: data[:uuid],
|
|
277
|
-
session_id: data[:session_id],
|
|
278
|
-
summary: data[:summary],
|
|
279
|
-
preceding_tool_use_ids: data[:precedingToolUseIds] || data[:preceding_tool_use_ids]
|
|
280
|
-
)
|
|
127
|
+
ToolUseSummaryMessage.new(data)
|
|
281
128
|
end
|
|
282
129
|
|
|
283
130
|
def self.parse_prompt_suggestion_message(data)
|
|
284
|
-
PromptSuggestionMessage.new(
|
|
285
|
-
uuid: data[:uuid],
|
|
286
|
-
session_id: data[:session_id],
|
|
287
|
-
suggestion: data[:suggestion]
|
|
288
|
-
)
|
|
131
|
+
PromptSuggestionMessage.new(data)
|
|
289
132
|
end
|
|
290
133
|
|
|
291
134
|
# Accepts blocks with either symbol or string keys — live CLI messages
|
|
@@ -261,7 +261,7 @@ module ClaudeAgentSDK
|
|
|
261
261
|
response: response_data
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
|
-
|
|
264
|
+
writeln(JSON.generate(success_response))
|
|
265
265
|
rescue Async::Stop
|
|
266
266
|
# Cancellation requested; respond with an error so the CLI can unblock.
|
|
267
267
|
cancelled_response = {
|
|
@@ -273,7 +273,7 @@ module ClaudeAgentSDK
|
|
|
273
273
|
error: 'Cancelled'
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
|
-
|
|
276
|
+
writeln(JSON.generate(cancelled_response))
|
|
277
277
|
rescue StandardError => e
|
|
278
278
|
# Send error response
|
|
279
279
|
error_response = {
|
|
@@ -285,7 +285,7 @@ module ClaudeAgentSDK
|
|
|
285
285
|
error: e.message
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
|
-
|
|
288
|
+
writeln(JSON.generate(error_response))
|
|
289
289
|
end
|
|
290
290
|
|
|
291
291
|
def handle_permission_request(request_data)
|
|
@@ -642,7 +642,7 @@ module ClaudeAgentSDK
|
|
|
642
642
|
request: request
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
-
|
|
645
|
+
writeln(JSON.generate(control_request))
|
|
646
646
|
|
|
647
647
|
# Wait for response with timeout (default 1200s to handle slow CLI startup)
|
|
648
648
|
Async do |task|
|
|
@@ -913,13 +913,8 @@ module ClaudeAgentSDK
|
|
|
913
913
|
def stream_input(stream)
|
|
914
914
|
stream.each do |message|
|
|
915
915
|
break if @closed
|
|
916
|
-
serialized =
|
|
917
|
-
|
|
918
|
-
else
|
|
919
|
-
message.to_s
|
|
920
|
-
end
|
|
921
|
-
serialized += "\n" unless serialized.end_with?("\n")
|
|
922
|
-
@transport.write(serialized)
|
|
916
|
+
serialized = message.is_a?(Hash) ? JSON.generate(message) : message.to_s
|
|
917
|
+
writeln(serialized)
|
|
923
918
|
end
|
|
924
919
|
rescue StandardError => e
|
|
925
920
|
# Log error but don't raise
|
|
@@ -928,6 +923,14 @@ module ClaudeAgentSDK
|
|
|
928
923
|
wait_for_result_and_end_input
|
|
929
924
|
end
|
|
930
925
|
|
|
926
|
+
def writeln(string)
|
|
927
|
+
write string.end_with?("\n") ? string : "#{string}\n"
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
def write(string)
|
|
931
|
+
@transport.write(string)
|
|
932
|
+
end
|
|
933
|
+
|
|
931
934
|
# Receive SDK messages (not control messages)
|
|
932
935
|
def receive_messages(&block)
|
|
933
936
|
return enum_for(:receive_messages) unless block
|