claude_agent 0.7.14 → 0.7.16
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/.claude/rules/conventions.md +66 -16
- data/CHANGELOG.md +20 -0
- data/CLAUDE.md +24 -4
- data/README.md +52 -1529
- data/SPEC.md +56 -29
- data/docs/architecture.md +339 -0
- data/docs/client.md +526 -0
- data/docs/configuration.md +571 -0
- data/docs/conversations.md +461 -0
- data/docs/errors.md +127 -0
- data/docs/events.md +225 -0
- data/docs/getting-started.md +310 -0
- data/docs/hooks.md +380 -0
- data/docs/logging.md +96 -0
- data/docs/mcp.md +308 -0
- data/docs/messages.md +871 -0
- data/docs/permissions.md +611 -0
- data/docs/queries.md +227 -0
- data/docs/sessions.md +335 -0
- data/lib/claude_agent/abort_controller.rb +24 -0
- data/lib/claude_agent/client/commands.rb +32 -0
- data/lib/claude_agent/client.rb +10 -4
- data/lib/claude_agent/configuration.rb +129 -0
- data/lib/claude_agent/control_protocol/commands.rb +28 -0
- data/lib/claude_agent/conversation.rb +37 -4
- data/lib/claude_agent/errors.rb +21 -4
- data/lib/claude_agent/event_handler.rb +14 -0
- data/lib/claude_agent/fork_session.rb +117 -0
- data/lib/claude_agent/hook_registry.rb +110 -0
- data/lib/claude_agent/hooks.rb +4 -0
- data/lib/claude_agent/mcp/server.rb +22 -0
- data/lib/claude_agent/mcp/tool.rb +24 -3
- data/lib/claude_agent/message.rb +93 -0
- data/lib/claude_agent/messages/streaming.rb +37 -0
- data/lib/claude_agent/options.rb +10 -0
- data/lib/claude_agent/permission_policy.rb +174 -0
- data/lib/claude_agent/permission_request.rb +17 -0
- data/lib/claude_agent/session.rb +100 -11
- data/lib/claude_agent/session_paths.rb +5 -2
- data/lib/claude_agent/turn_result.rb +20 -2
- data/lib/claude_agent/types/sessions.rb +8 -0
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +187 -0
- data/sig/claude_agent.rbs +38 -1
- metadata +20 -1
data/docs/messages.md
ADDED
|
@@ -0,0 +1,871 @@
|
|
|
1
|
+
# Messages & Content Blocks Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for all message types and content blocks in the ClaudeAgent Ruby SDK.
|
|
4
|
+
|
|
5
|
+
All types are immutable (`Data.define`, frozen at construction). All types include the `ClaudeAgent::Message` module.
|
|
6
|
+
|
|
7
|
+
## Message Module
|
|
8
|
+
|
|
9
|
+
Every message and content block type includes `ClaudeAgent::Message`, which provides:
|
|
10
|
+
|
|
11
|
+
| Method | Returns | Description |
|
|
12
|
+
|--------------------|-----------|--------------------------------------------------------------------------------------------------------|
|
|
13
|
+
| `text_content` | `String` | Universal text extraction. Works on any message or block type. Returns `""` when no text is available. |
|
|
14
|
+
| `session_message?` | `Boolean` | `true` if the message has non-nil `uuid` and `session_id`. |
|
|
15
|
+
| `identifiable?` | `Boolean` | `true` if the message has a non-nil `uuid`. |
|
|
16
|
+
| `deconstruct_keys` | `Hash` | Injects `:type` as a virtual key for pattern matching. |
|
|
17
|
+
|
|
18
|
+
### text_content behavior by type
|
|
19
|
+
|
|
20
|
+
| Type | Extracts from |
|
|
21
|
+
|------------------------------------|-----------------------------------------------------|
|
|
22
|
+
| `AssistantMessage` | Concatenated `TextBlock` text via `#text` |
|
|
23
|
+
| `UserMessage`, `UserMessageReplay` | `content` if it is a `String` |
|
|
24
|
+
| `TextBlock` | `text` |
|
|
25
|
+
| `ThinkingBlock` | `thinking` |
|
|
26
|
+
| `StreamEvent` | `delta_text` |
|
|
27
|
+
| `GenericMessage` | `raw[:text]` or `raw["text"]` |
|
|
28
|
+
| Everything else | `text` if the object responds to it, otherwise `""` |
|
|
29
|
+
|
|
30
|
+
## Pattern Matching
|
|
31
|
+
|
|
32
|
+
The `Message` module overrides `deconstruct_keys` to inject a `:type` virtual key, enabling Ruby pattern matching on message types:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
case message
|
|
36
|
+
in { type: :assistant }
|
|
37
|
+
puts message.text_content
|
|
38
|
+
in { type: :result, is_error: true }
|
|
39
|
+
warn "Error: #{message.errors}"
|
|
40
|
+
in { type: :result }
|
|
41
|
+
puts "Cost: $#{message.total_cost_usd}"
|
|
42
|
+
in { type: :system, subtype: "init" }
|
|
43
|
+
puts "Session initialized"
|
|
44
|
+
in { type: :stream_event }
|
|
45
|
+
print message.delta_text
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
You can also combine pattern matching with field extraction:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
case message
|
|
53
|
+
in { type: :assistant, model: }
|
|
54
|
+
puts "Model: #{model}"
|
|
55
|
+
in { type: :hook_response, outcome: "error", stderr: }
|
|
56
|
+
warn stderr
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Message Types
|
|
63
|
+
|
|
64
|
+
22 message types, grouped by category.
|
|
65
|
+
|
|
66
|
+
### Conversation Messages
|
|
67
|
+
|
|
68
|
+
#### UserMessage
|
|
69
|
+
|
|
70
|
+
User message sent to Claude.
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
UserMessage = Data.define(:content, :uuid, :session_id, :parent_tool_use_id)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| Field | Type | Default |
|
|
77
|
+
|----------------------|---------------------|----------|
|
|
78
|
+
| `content` | `String` or `Array` | required |
|
|
79
|
+
| `uuid` | `String, nil` | `nil` |
|
|
80
|
+
| `session_id` | `String, nil` | `nil` |
|
|
81
|
+
| `parent_tool_use_id` | `String, nil` | `nil` |
|
|
82
|
+
|
|
83
|
+
Methods:
|
|
84
|
+
|
|
85
|
+
- `type` -- `:user`
|
|
86
|
+
- `text` -- returns `content` if it is a `String`, else `nil`
|
|
87
|
+
- `replay?` -- always `false`
|
|
88
|
+
|
|
89
|
+
#### UserMessageReplay
|
|
90
|
+
|
|
91
|
+
Replayed user message from a resumed session.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
UserMessageReplay = Data.define(
|
|
95
|
+
:content, :uuid, :session_id, :parent_tool_use_id,
|
|
96
|
+
:is_replay, :is_synthetic, :tool_use_result
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Field | Type | Default |
|
|
101
|
+
|----------------------|---------------------|----------|
|
|
102
|
+
| `content` | `String` or `Array` | required |
|
|
103
|
+
| `uuid` | `String, nil` | `nil` |
|
|
104
|
+
| `session_id` | `String, nil` | `nil` |
|
|
105
|
+
| `parent_tool_use_id` | `String, nil` | `nil` |
|
|
106
|
+
| `is_replay` | `Boolean` | `true` |
|
|
107
|
+
| `is_synthetic` | `Boolean, nil` | `nil` |
|
|
108
|
+
| `tool_use_result` | `Hash, nil` | `nil` |
|
|
109
|
+
|
|
110
|
+
Methods:
|
|
111
|
+
|
|
112
|
+
- `type` -- `:user`
|
|
113
|
+
- `text` -- returns `content` if it is a `String`, else `nil`
|
|
114
|
+
- `replay?` -- `true` if `is_replay == true`
|
|
115
|
+
- `synthetic?` -- `true` if `is_synthetic == true`
|
|
116
|
+
|
|
117
|
+
#### AssistantMessage
|
|
118
|
+
|
|
119
|
+
Response from Claude containing content blocks.
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
AssistantMessage = Data.define(:content, :model, :uuid, :session_id, :error, :parent_tool_use_id)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
| Field | Type | Default |
|
|
126
|
+
|----------------------|-----------------------|----------|
|
|
127
|
+
| `content` | `Array<ContentBlock>` | required |
|
|
128
|
+
| `model` | `String` | required |
|
|
129
|
+
| `uuid` | `String, nil` | `nil` |
|
|
130
|
+
| `session_id` | `String, nil` | `nil` |
|
|
131
|
+
| `error` | `Hash, nil` | `nil` |
|
|
132
|
+
| `parent_tool_use_id` | `String, nil` | `nil` |
|
|
133
|
+
|
|
134
|
+
Methods:
|
|
135
|
+
|
|
136
|
+
- `type` -- `:assistant`
|
|
137
|
+
- `text` -- concatenated text from all `TextBlock`s
|
|
138
|
+
- `thinking` -- concatenated text from all `ThinkingBlock`s
|
|
139
|
+
- `tool_uses` -- `Array<ToolUseBlock>` from content
|
|
140
|
+
- `has_tool_use?` -- `true` if any content block is a `ToolUseBlock`
|
|
141
|
+
|
|
142
|
+
```ruby
|
|
143
|
+
msg.text # => "Hello, world!"
|
|
144
|
+
msg.tool_uses # => [#<ToolUseBlock id="tool_1" name="Read" ...>]
|
|
145
|
+
msg.has_tool_use? # => true
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Result
|
|
149
|
+
|
|
150
|
+
#### ResultMessage
|
|
151
|
+
|
|
152
|
+
Final message with cost, usage, and outcome info.
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
ResultMessage = Data.define(
|
|
156
|
+
:subtype, :duration_ms, :duration_api_ms, :is_error, :num_turns,
|
|
157
|
+
:session_id, :uuid, :total_cost_usd, :usage, :result, :structured_output,
|
|
158
|
+
:errors, :permission_denials, :model_usage, :stop_reason, :fast_mode_state
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Field | Type | Default |
|
|
163
|
+
|----------------------|----------------------|----------|
|
|
164
|
+
| `subtype` | `String` | required |
|
|
165
|
+
| `duration_ms` | `Integer` | required |
|
|
166
|
+
| `duration_api_ms` | `Integer` | required |
|
|
167
|
+
| `is_error` | `Boolean` | required |
|
|
168
|
+
| `num_turns` | `Integer` | required |
|
|
169
|
+
| `session_id` | `String` | required |
|
|
170
|
+
| `uuid` | `String, nil` | `nil` |
|
|
171
|
+
| `total_cost_usd` | `Float, nil` | `nil` |
|
|
172
|
+
| `usage` | `Hash, nil` | `nil` |
|
|
173
|
+
| `result` | `String, nil` | `nil` |
|
|
174
|
+
| `structured_output` | `Hash, nil` | `nil` |
|
|
175
|
+
| `errors` | `Array<String>, nil` | `nil` |
|
|
176
|
+
| `permission_denials` | `Array, nil` | `nil` |
|
|
177
|
+
| `model_usage` | `Hash, nil` | `nil` |
|
|
178
|
+
| `stop_reason` | `String, nil` | `nil` |
|
|
179
|
+
| `fast_mode_state` | `Hash, nil` | `nil` |
|
|
180
|
+
|
|
181
|
+
Methods:
|
|
182
|
+
|
|
183
|
+
- `type` -- `:result`
|
|
184
|
+
- `error?` -- `true` if `is_error`
|
|
185
|
+
- `success?` -- `true` if not `is_error`
|
|
186
|
+
|
|
187
|
+
### System & Status
|
|
188
|
+
|
|
189
|
+
#### SystemMessage
|
|
190
|
+
|
|
191
|
+
Internal system event (e.g., session init).
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
SystemMessage = Data.define(:subtype, :data)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
| Field | Type | Default |
|
|
198
|
+
|-----------|----------|----------|
|
|
199
|
+
| `subtype` | `String` | required |
|
|
200
|
+
| `data` | `Hash` | required |
|
|
201
|
+
|
|
202
|
+
Methods:
|
|
203
|
+
|
|
204
|
+
- `type` -- `:system`
|
|
205
|
+
|
|
206
|
+
#### CompactBoundaryMessage
|
|
207
|
+
|
|
208
|
+
Conversation compaction marker.
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
CompactBoundaryMessage = Data.define(:uuid, :session_id, :compact_metadata)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
| Field | Type | Default |
|
|
215
|
+
|--------------------|----------|----------|
|
|
216
|
+
| `uuid` | `String` | required |
|
|
217
|
+
| `session_id` | `String` | required |
|
|
218
|
+
| `compact_metadata` | `Hash` | required |
|
|
219
|
+
|
|
220
|
+
Methods:
|
|
221
|
+
|
|
222
|
+
- `type` -- `:compact_boundary`
|
|
223
|
+
- `trigger` -- compaction trigger type (`"manual"` or `"auto"`)
|
|
224
|
+
- `pre_tokens` -- token count before compaction
|
|
225
|
+
|
|
226
|
+
#### StatusMessage
|
|
227
|
+
|
|
228
|
+
Session status report (e.g., `"compacting"`).
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
StatusMessage = Data.define(:uuid, :session_id, :status, :permission_mode)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
| Field | Type | Default |
|
|
235
|
+
|-------------------|---------------|----------|
|
|
236
|
+
| `uuid` | `String` | required |
|
|
237
|
+
| `session_id` | `String` | required |
|
|
238
|
+
| `status` | `String` | required |
|
|
239
|
+
| `permission_mode` | `String, nil` | `nil` |
|
|
240
|
+
|
|
241
|
+
Methods:
|
|
242
|
+
|
|
243
|
+
- `type` -- `:status`
|
|
244
|
+
|
|
245
|
+
### Streaming
|
|
246
|
+
|
|
247
|
+
#### StreamEvent
|
|
248
|
+
|
|
249
|
+
Partial message during streaming.
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
StreamEvent = Data.define(:uuid, :session_id, :event, :parent_tool_use_id)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
| Field | Type | Default |
|
|
256
|
+
|----------------------|---------------|----------|
|
|
257
|
+
| `uuid` | `String` | required |
|
|
258
|
+
| `session_id` | `String` | required |
|
|
259
|
+
| `event` | `Hash` | required |
|
|
260
|
+
| `parent_tool_use_id` | `String, nil` | `nil` |
|
|
261
|
+
|
|
262
|
+
Methods:
|
|
263
|
+
|
|
264
|
+
- `type` -- `:stream_event`
|
|
265
|
+
- `event_type` -- raw event type string (e.g., `"content_block_delta"`)
|
|
266
|
+
- `delta_text` -- text delta content, or `nil` if not a text delta
|
|
267
|
+
- `delta_type` -- delta type string (e.g., `"text_delta"`, `"thinking_delta"`)
|
|
268
|
+
- `thinking_text` -- thinking delta text, or `nil` if not a thinking delta
|
|
269
|
+
- `content_index` -- content block index within the message
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
event.delta_text # => "Hello"
|
|
273
|
+
event.delta_type # => "text_delta"
|
|
274
|
+
event.thinking_text # => nil (only set for thinking deltas)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
#### RateLimitEvent
|
|
278
|
+
|
|
279
|
+
Rate limit status and utilization info.
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
RateLimitEvent = Data.define(:rate_limit_info, :uuid, :session_id)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
| Field | Type | Default |
|
|
286
|
+
|-------------------|---------------|----------|
|
|
287
|
+
| `rate_limit_info` | `Hash` | required |
|
|
288
|
+
| `uuid` | `String, nil` | `nil` |
|
|
289
|
+
| `session_id` | `String, nil` | `nil` |
|
|
290
|
+
|
|
291
|
+
Methods:
|
|
292
|
+
|
|
293
|
+
- `type` -- `:rate_limit_event`
|
|
294
|
+
- `status` -- rate limit status string (e.g., `"allowed_warning"`)
|
|
295
|
+
|
|
296
|
+
#### PromptSuggestionMessage
|
|
297
|
+
|
|
298
|
+
Suggested prompt for the user.
|
|
299
|
+
|
|
300
|
+
```ruby
|
|
301
|
+
PromptSuggestionMessage = Data.define(:uuid, :session_id, :suggestion)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
| Field | Type | Default |
|
|
305
|
+
|--------------|---------------|----------|
|
|
306
|
+
| `uuid` | `String, nil` | `nil` |
|
|
307
|
+
| `session_id` | `String, nil` | `nil` |
|
|
308
|
+
| `suggestion` | `String` | required |
|
|
309
|
+
|
|
310
|
+
Methods:
|
|
311
|
+
|
|
312
|
+
- `type` -- `:prompt_suggestion`
|
|
313
|
+
|
|
314
|
+
### Tool Lifecycle
|
|
315
|
+
|
|
316
|
+
#### ToolProgressMessage
|
|
317
|
+
|
|
318
|
+
Progress during long-running tool execution.
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
ToolProgressMessage = Data.define(
|
|
322
|
+
:uuid, :session_id, :tool_use_id, :tool_name,
|
|
323
|
+
:parent_tool_use_id, :elapsed_time_seconds, :task_id
|
|
324
|
+
)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
| Field | Type | Default |
|
|
328
|
+
|------------------------|---------------|----------|
|
|
329
|
+
| `uuid` | `String` | required |
|
|
330
|
+
| `session_id` | `String` | required |
|
|
331
|
+
| `tool_use_id` | `String` | required |
|
|
332
|
+
| `tool_name` | `String` | required |
|
|
333
|
+
| `elapsed_time_seconds` | `Float` | required |
|
|
334
|
+
| `parent_tool_use_id` | `String, nil` | `nil` |
|
|
335
|
+
| `task_id` | `String, nil` | `nil` |
|
|
336
|
+
|
|
337
|
+
Methods:
|
|
338
|
+
|
|
339
|
+
- `type` -- `:tool_progress`
|
|
340
|
+
|
|
341
|
+
#### ToolUseSummaryMessage
|
|
342
|
+
|
|
343
|
+
Summary of tool use for collapsed display.
|
|
344
|
+
|
|
345
|
+
```ruby
|
|
346
|
+
ToolUseSummaryMessage = Data.define(:uuid, :session_id, :summary, :preceding_tool_use_ids)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
| Field | Type | Default |
|
|
350
|
+
|--------------------------|-----------------|----------|
|
|
351
|
+
| `uuid` | `String` | required |
|
|
352
|
+
| `session_id` | `String` | required |
|
|
353
|
+
| `summary` | `String` | required |
|
|
354
|
+
| `preceding_tool_use_ids` | `Array<String>` | `[]` |
|
|
355
|
+
|
|
356
|
+
Methods:
|
|
357
|
+
|
|
358
|
+
- `type` -- `:tool_use_summary`
|
|
359
|
+
|
|
360
|
+
#### LocalCommandOutputMessage
|
|
361
|
+
|
|
362
|
+
Output from a local command execution.
|
|
363
|
+
|
|
364
|
+
```ruby
|
|
365
|
+
LocalCommandOutputMessage = Data.define(:uuid, :session_id, :content)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
| Field | Type | Default |
|
|
369
|
+
|--------------|----------|---------|
|
|
370
|
+
| `uuid` | `String` | `""` |
|
|
371
|
+
| `session_id` | `String` | `""` |
|
|
372
|
+
| `content` | `String` | `""` |
|
|
373
|
+
|
|
374
|
+
Methods:
|
|
375
|
+
|
|
376
|
+
- `type` -- `:local_command_output`
|
|
377
|
+
|
|
378
|
+
### Hook Lifecycle
|
|
379
|
+
|
|
380
|
+
#### HookStartedMessage
|
|
381
|
+
|
|
382
|
+
Sent when a hook execution starts.
|
|
383
|
+
|
|
384
|
+
```ruby
|
|
385
|
+
HookStartedMessage = Data.define(:uuid, :session_id, :hook_id, :hook_name, :hook_event)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
| Field | Type | Default |
|
|
389
|
+
|--------------|----------|----------|
|
|
390
|
+
| `uuid` | `String` | required |
|
|
391
|
+
| `session_id` | `String` | required |
|
|
392
|
+
| `hook_id` | `String` | required |
|
|
393
|
+
| `hook_name` | `String` | required |
|
|
394
|
+
| `hook_event` | `String` | required |
|
|
395
|
+
|
|
396
|
+
Methods:
|
|
397
|
+
|
|
398
|
+
- `type` -- `:hook_started`
|
|
399
|
+
|
|
400
|
+
#### HookProgressMessage
|
|
401
|
+
|
|
402
|
+
Progress during hook execution.
|
|
403
|
+
|
|
404
|
+
```ruby
|
|
405
|
+
HookProgressMessage = Data.define(
|
|
406
|
+
:uuid, :session_id, :hook_id, :hook_name, :hook_event,
|
|
407
|
+
:stdout, :stderr, :output
|
|
408
|
+
)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
| Field | Type | Default |
|
|
412
|
+
|--------------|----------|----------|
|
|
413
|
+
| `uuid` | `String` | required |
|
|
414
|
+
| `session_id` | `String` | required |
|
|
415
|
+
| `hook_id` | `String` | required |
|
|
416
|
+
| `hook_name` | `String` | required |
|
|
417
|
+
| `hook_event` | `String` | required |
|
|
418
|
+
| `stdout` | `String` | `""` |
|
|
419
|
+
| `stderr` | `String` | `""` |
|
|
420
|
+
| `output` | `String` | `""` |
|
|
421
|
+
|
|
422
|
+
Methods:
|
|
423
|
+
|
|
424
|
+
- `type` -- `:hook_progress`
|
|
425
|
+
|
|
426
|
+
#### HookResponseMessage
|
|
427
|
+
|
|
428
|
+
Final result of a hook execution.
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
HookResponseMessage = Data.define(
|
|
432
|
+
:uuid, :session_id, :hook_id, :hook_name, :hook_event,
|
|
433
|
+
:stdout, :stderr, :output, :exit_code, :outcome
|
|
434
|
+
)
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
| Field | Type | Default |
|
|
438
|
+
|--------------|----------------|----------|
|
|
439
|
+
| `uuid` | `String` | required |
|
|
440
|
+
| `session_id` | `String` | required |
|
|
441
|
+
| `hook_id` | `String, nil` | `nil` |
|
|
442
|
+
| `hook_name` | `String` | required |
|
|
443
|
+
| `hook_event` | `String` | required |
|
|
444
|
+
| `stdout` | `String` | `""` |
|
|
445
|
+
| `stderr` | `String` | `""` |
|
|
446
|
+
| `output` | `String` | `""` |
|
|
447
|
+
| `exit_code` | `Integer, nil` | `nil` |
|
|
448
|
+
| `outcome` | `String, nil` | `nil` |
|
|
449
|
+
|
|
450
|
+
Methods:
|
|
451
|
+
|
|
452
|
+
- `type` -- `:hook_response`
|
|
453
|
+
- `success?` -- `true` if `outcome == "success"`
|
|
454
|
+
- `error?` -- `true` if `outcome == "error"`
|
|
455
|
+
- `cancelled?` -- `true` if `outcome == "cancelled"`
|
|
456
|
+
|
|
457
|
+
### Task Lifecycle
|
|
458
|
+
|
|
459
|
+
#### TaskStartedMessage
|
|
460
|
+
|
|
461
|
+
Sent when a new task (subagent) starts.
|
|
462
|
+
|
|
463
|
+
```ruby
|
|
464
|
+
TaskStartedMessage = Data.define(
|
|
465
|
+
:uuid, :session_id, :task_id, :tool_use_id,
|
|
466
|
+
:description, :task_type, :prompt
|
|
467
|
+
)
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
| Field | Type | Default |
|
|
471
|
+
|---------------|---------------|----------|
|
|
472
|
+
| `uuid` | `String` | required |
|
|
473
|
+
| `session_id` | `String` | required |
|
|
474
|
+
| `task_id` | `String` | required |
|
|
475
|
+
| `tool_use_id` | `String, nil` | `nil` |
|
|
476
|
+
| `description` | `String, nil` | `nil` |
|
|
477
|
+
| `task_type` | `String, nil` | `nil` |
|
|
478
|
+
| `prompt` | `String, nil` | `nil` |
|
|
479
|
+
|
|
480
|
+
Methods:
|
|
481
|
+
|
|
482
|
+
- `type` -- `:task_started`
|
|
483
|
+
|
|
484
|
+
#### TaskProgressMessage
|
|
485
|
+
|
|
486
|
+
Progress during background task (subagent) execution.
|
|
487
|
+
|
|
488
|
+
```ruby
|
|
489
|
+
TaskProgressMessage = Data.define(
|
|
490
|
+
:uuid, :session_id, :task_id, :tool_use_id,
|
|
491
|
+
:description, :usage, :last_tool_name, :summary
|
|
492
|
+
)
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
| Field | Type | Default |
|
|
496
|
+
|------------------|---------------|----------|
|
|
497
|
+
| `uuid` | `String` | required |
|
|
498
|
+
| `session_id` | `String` | required |
|
|
499
|
+
| `task_id` | `String` | required |
|
|
500
|
+
| `description` | `String` | required |
|
|
501
|
+
| `tool_use_id` | `String, nil` | `nil` |
|
|
502
|
+
| `usage` | `Hash, nil` | `nil` |
|
|
503
|
+
| `last_tool_name` | `String, nil` | `nil` |
|
|
504
|
+
| `summary` | `String, nil` | `nil` |
|
|
505
|
+
|
|
506
|
+
Methods:
|
|
507
|
+
|
|
508
|
+
- `type` -- `:task_progress`
|
|
509
|
+
|
|
510
|
+
#### TaskNotificationMessage
|
|
511
|
+
|
|
512
|
+
Sent when a background task completes, fails, or is stopped.
|
|
513
|
+
|
|
514
|
+
```ruby
|
|
515
|
+
TaskNotificationMessage = Data.define(
|
|
516
|
+
:uuid, :session_id, :task_id, :status,
|
|
517
|
+
:output_file, :summary, :tool_use_id, :usage
|
|
518
|
+
)
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
| Field | Type | Default |
|
|
522
|
+
|---------------|---------------|----------|
|
|
523
|
+
| `uuid` | `String` | required |
|
|
524
|
+
| `session_id` | `String` | required |
|
|
525
|
+
| `task_id` | `String` | required |
|
|
526
|
+
| `status` | `String` | required |
|
|
527
|
+
| `output_file` | `String` | required |
|
|
528
|
+
| `summary` | `String` | required |
|
|
529
|
+
| `tool_use_id` | `String, nil` | `nil` |
|
|
530
|
+
| `usage` | `Hash, nil` | `nil` |
|
|
531
|
+
|
|
532
|
+
Methods:
|
|
533
|
+
|
|
534
|
+
- `type` -- `:task_notification`
|
|
535
|
+
- `completed?` -- `true` if `status == "completed"`
|
|
536
|
+
- `failed?` -- `true` if `status == "failed"`
|
|
537
|
+
- `stopped?` -- `true` if `status == "stopped"`
|
|
538
|
+
|
|
539
|
+
### Other
|
|
540
|
+
|
|
541
|
+
#### FilesPersistedEvent
|
|
542
|
+
|
|
543
|
+
Sent when files are persisted to storage.
|
|
544
|
+
|
|
545
|
+
```ruby
|
|
546
|
+
FilesPersistedEvent = Data.define(:uuid, :session_id, :files, :failed, :processed_at)
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
| Field | Type | Default |
|
|
550
|
+
|----------------|---------------|----------|
|
|
551
|
+
| `uuid` | `String` | required |
|
|
552
|
+
| `session_id` | `String` | required |
|
|
553
|
+
| `files` | `Array<Hash>` | `[]` |
|
|
554
|
+
| `failed` | `Array<Hash>` | `[]` |
|
|
555
|
+
| `processed_at` | `String, nil` | `nil` |
|
|
556
|
+
|
|
557
|
+
Methods:
|
|
558
|
+
|
|
559
|
+
- `type` -- `:files_persisted`
|
|
560
|
+
|
|
561
|
+
#### ElicitationCompleteMessage
|
|
562
|
+
|
|
563
|
+
Sent when an MCP server elicitation request completes.
|
|
564
|
+
|
|
565
|
+
```ruby
|
|
566
|
+
ElicitationCompleteMessage = Data.define(:uuid, :session_id, :mcp_server_name, :elicitation_id)
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
| Field | Type | Default |
|
|
570
|
+
|-------------------|----------|---------|
|
|
571
|
+
| `uuid` | `String` | `""` |
|
|
572
|
+
| `session_id` | `String` | `""` |
|
|
573
|
+
| `mcp_server_name` | `String` | `""` |
|
|
574
|
+
| `elicitation_id` | `String` | `""` |
|
|
575
|
+
|
|
576
|
+
Methods:
|
|
577
|
+
|
|
578
|
+
- `type` -- `:elicitation_complete`
|
|
579
|
+
|
|
580
|
+
#### AuthStatusMessage
|
|
581
|
+
|
|
582
|
+
Authentication status during login flows.
|
|
583
|
+
|
|
584
|
+
```ruby
|
|
585
|
+
AuthStatusMessage = Data.define(:uuid, :session_id, :is_authenticating, :output, :error)
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
| Field | Type | Default |
|
|
589
|
+
|---------------------|---------------|----------|
|
|
590
|
+
| `uuid` | `String` | required |
|
|
591
|
+
| `session_id` | `String` | required |
|
|
592
|
+
| `is_authenticating` | `Boolean` | required |
|
|
593
|
+
| `output` | `Array` | `[]` |
|
|
594
|
+
| `error` | `String, nil` | `nil` |
|
|
595
|
+
|
|
596
|
+
Methods:
|
|
597
|
+
|
|
598
|
+
- `type` -- `:auth_status`
|
|
599
|
+
|
|
600
|
+
#### GenericMessage
|
|
601
|
+
|
|
602
|
+
Catch-all for unknown/future protocol message types. Supports dynamic field access.
|
|
603
|
+
|
|
604
|
+
```ruby
|
|
605
|
+
GenericMessage = Data.define(:message_type, :raw)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
| Field | Type | Default |
|
|
609
|
+
|----------------|----------|----------|
|
|
610
|
+
| `message_type` | `String` | required |
|
|
611
|
+
| `raw` | `Hash` | required |
|
|
612
|
+
|
|
613
|
+
Methods:
|
|
614
|
+
|
|
615
|
+
- `type` -- `message_type` as a symbol, or `:unknown`
|
|
616
|
+
- `to_h` -- returns `raw`
|
|
617
|
+
- `[](key)` -- hash-style access into `raw`
|
|
618
|
+
- `method_missing` -- dynamic field access into `raw`
|
|
619
|
+
|
|
620
|
+
```ruby
|
|
621
|
+
msg = GenericMessage.new(message_type: "fancy_new", raw: { data: "hello" })
|
|
622
|
+
msg.type # => :fancy_new
|
|
623
|
+
msg[:data] # => "hello"
|
|
624
|
+
msg.data # => "hello"
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
---
|
|
628
|
+
|
|
629
|
+
## Content Blocks
|
|
630
|
+
|
|
631
|
+
8 content block types. Found inside `AssistantMessage#content` arrays.
|
|
632
|
+
|
|
633
|
+
### TextBlock
|
|
634
|
+
|
|
635
|
+
Plain text content.
|
|
636
|
+
|
|
637
|
+
```ruby
|
|
638
|
+
TextBlock = Data.define(:text)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
| Field | Type | Default |
|
|
642
|
+
|--------|----------|----------|
|
|
643
|
+
| `text` | `String` | required |
|
|
644
|
+
|
|
645
|
+
Methods:
|
|
646
|
+
|
|
647
|
+
- `type` -- `:text`
|
|
648
|
+
- `to_h` -- `{ type: "text", text: text }`
|
|
649
|
+
|
|
650
|
+
### ThinkingBlock
|
|
651
|
+
|
|
652
|
+
Extended thinking content.
|
|
653
|
+
|
|
654
|
+
```ruby
|
|
655
|
+
ThinkingBlock = Data.define(:thinking, :signature)
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
| Field | Type | Default |
|
|
659
|
+
|-------------|----------|----------|
|
|
660
|
+
| `thinking` | `String` | required |
|
|
661
|
+
| `signature` | `String` | required |
|
|
662
|
+
|
|
663
|
+
Methods:
|
|
664
|
+
|
|
665
|
+
- `type` -- `:thinking`
|
|
666
|
+
- `to_h` -- `{ type: "thinking", thinking: thinking, signature: signature }`
|
|
667
|
+
|
|
668
|
+
### ToolUseBlock
|
|
669
|
+
|
|
670
|
+
Tool use request from Claude.
|
|
671
|
+
|
|
672
|
+
```ruby
|
|
673
|
+
ToolUseBlock = Data.define(:id, :name, :input)
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
| Field | Type | Default |
|
|
677
|
+
|---------|----------|----------|
|
|
678
|
+
| `id` | `String` | required |
|
|
679
|
+
| `name` | `String` | required |
|
|
680
|
+
| `input` | `Hash` | required |
|
|
681
|
+
|
|
682
|
+
Methods:
|
|
683
|
+
|
|
684
|
+
- `type` -- `:tool_use`
|
|
685
|
+
- `file_path` -- file path for file-based tools (`Read`, `Write`, `Edit`, `NotebookEdit`), else `nil`
|
|
686
|
+
- `display_label` -- one-line human-readable label (e.g., `"Read config/app.rb"`, `"Bash: ls -la"`)
|
|
687
|
+
- `summary(max: 60)` -- detailed summary, truncated to `max` characters
|
|
688
|
+
- `to_h` -- `{ type: "tool_use", id: id, name: name, input: input }`
|
|
689
|
+
|
|
690
|
+
```ruby
|
|
691
|
+
block = ToolUseBlock.new(id: "tool_1", name: "Read", input: { file_path: "/tmp/file.rb" })
|
|
692
|
+
block.file_path # => "/tmp/file.rb"
|
|
693
|
+
block.display_label # => "Read file.rb"
|
|
694
|
+
block.summary # => "Read: /tmp/file.rb"
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### ToolResultBlock
|
|
698
|
+
|
|
699
|
+
Result returned from a tool execution.
|
|
700
|
+
|
|
701
|
+
```ruby
|
|
702
|
+
ToolResultBlock = Data.define(:tool_use_id, :content, :is_error)
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
| Field | Type | Default |
|
|
706
|
+
|---------------|----------------|----------|
|
|
707
|
+
| `tool_use_id` | `String` | required |
|
|
708
|
+
| `content` | `String, nil` | `nil` |
|
|
709
|
+
| `is_error` | `Boolean, nil` | `nil` |
|
|
710
|
+
|
|
711
|
+
Methods:
|
|
712
|
+
|
|
713
|
+
- `type` -- `:tool_result`
|
|
714
|
+
- `to_h` -- includes `content` and `is_error` only when non-nil
|
|
715
|
+
|
|
716
|
+
### ServerToolUseBlock
|
|
717
|
+
|
|
718
|
+
Tool use request for an MCP server tool.
|
|
719
|
+
|
|
720
|
+
```ruby
|
|
721
|
+
ServerToolUseBlock = Data.define(:id, :name, :input, :server_name)
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
| Field | Type | Default |
|
|
725
|
+
|---------------|----------|----------|
|
|
726
|
+
| `id` | `String` | required |
|
|
727
|
+
| `name` | `String` | required |
|
|
728
|
+
| `input` | `Hash` | required |
|
|
729
|
+
| `server_name` | `String` | required |
|
|
730
|
+
|
|
731
|
+
Methods:
|
|
732
|
+
|
|
733
|
+
- `type` -- `:server_tool_use`
|
|
734
|
+
- `file_path` -- file path for file-based tools, else `nil`
|
|
735
|
+
- `display_label` -- label with server context (e.g., `"my-server/tool-name"`)
|
|
736
|
+
- `summary(max: 60)` -- detailed summary with server context, truncated
|
|
737
|
+
- `to_h` -- `{ type: "server_tool_use", id: id, name: name, input: input, server_name: server_name }`
|
|
738
|
+
|
|
739
|
+
### ServerToolResultBlock
|
|
740
|
+
|
|
741
|
+
Result from an MCP server tool execution.
|
|
742
|
+
|
|
743
|
+
```ruby
|
|
744
|
+
ServerToolResultBlock = Data.define(:tool_use_id, :content, :is_error, :server_name)
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
| Field | Type | Default |
|
|
748
|
+
|---------------|----------------|----------|
|
|
749
|
+
| `tool_use_id` | `String` | required |
|
|
750
|
+
| `server_name` | `String` | required |
|
|
751
|
+
| `content` | `String, nil` | `nil` |
|
|
752
|
+
| `is_error` | `Boolean, nil` | `nil` |
|
|
753
|
+
|
|
754
|
+
Methods:
|
|
755
|
+
|
|
756
|
+
- `type` -- `:server_tool_result`
|
|
757
|
+
- `to_h` -- includes `content` and `is_error` only when non-nil
|
|
758
|
+
|
|
759
|
+
### ImageContentBlock
|
|
760
|
+
|
|
761
|
+
Image content (base64-encoded or URL-sourced).
|
|
762
|
+
|
|
763
|
+
```ruby
|
|
764
|
+
ImageContentBlock = Data.define(:source)
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
| Field | Type | Default |
|
|
768
|
+
|----------|--------|----------|
|
|
769
|
+
| `source` | `Hash` | required |
|
|
770
|
+
|
|
771
|
+
Methods:
|
|
772
|
+
|
|
773
|
+
- `type` -- `:image`
|
|
774
|
+
- `source_type` -- `"base64"` or `"url"`
|
|
775
|
+
- `media_type` -- MIME type (e.g., `"image/png"`)
|
|
776
|
+
- `data` -- base64-encoded image data
|
|
777
|
+
- `url` -- URL for URL-sourced images
|
|
778
|
+
- `to_h` -- `{ type: "image", source: source }`
|
|
779
|
+
|
|
780
|
+
```ruby
|
|
781
|
+
block = ImageContentBlock.new(source: { type: "base64", media_type: "image/png", data: "..." })
|
|
782
|
+
block.source_type # => "base64"
|
|
783
|
+
block.media_type # => "image/png"
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
### GenericBlock
|
|
787
|
+
|
|
788
|
+
Catch-all for unknown/future content block types. Supports dynamic field access.
|
|
789
|
+
|
|
790
|
+
```ruby
|
|
791
|
+
GenericBlock = Data.define(:block_type, :raw)
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
| Field | Type | Default |
|
|
795
|
+
|--------------|----------|----------|
|
|
796
|
+
| `block_type` | `String` | required |
|
|
797
|
+
| `raw` | `Hash` | required |
|
|
798
|
+
|
|
799
|
+
Methods:
|
|
800
|
+
|
|
801
|
+
- `type` -- `block_type` as a symbol, or `:unknown`
|
|
802
|
+
- `to_h` -- returns `raw`
|
|
803
|
+
- `[](key)` -- hash-style access into `raw`
|
|
804
|
+
- `method_missing` -- dynamic field access into `raw`
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## Common Patterns
|
|
809
|
+
|
|
810
|
+
### Iterating content blocks with case
|
|
811
|
+
|
|
812
|
+
```ruby
|
|
813
|
+
message.content.each do |block|
|
|
814
|
+
case block
|
|
815
|
+
when ClaudeAgent::TextBlock
|
|
816
|
+
puts block.text
|
|
817
|
+
when ClaudeAgent::ThinkingBlock
|
|
818
|
+
puts "[thinking] #{block.thinking}"
|
|
819
|
+
when ClaudeAgent::ToolUseBlock
|
|
820
|
+
puts "Tool: #{block.display_label}"
|
|
821
|
+
when ClaudeAgent::ServerToolUseBlock
|
|
822
|
+
puts "MCP Tool: #{block.display_label}"
|
|
823
|
+
when ClaudeAgent::ImageContentBlock
|
|
824
|
+
puts "Image (#{block.media_type})"
|
|
825
|
+
else
|
|
826
|
+
puts "Unknown block: #{block.type}"
|
|
827
|
+
end
|
|
828
|
+
end
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
### Filtering message streams
|
|
832
|
+
|
|
833
|
+
```ruby
|
|
834
|
+
messages = ClaudeAgent.query(prompt: "Hello", options: options).to_a
|
|
835
|
+
|
|
836
|
+
# Find the final result
|
|
837
|
+
result = messages.find { |m| m.is_a?(ClaudeAgent::ResultMessage) }
|
|
838
|
+
|
|
839
|
+
# Collect all assistant text
|
|
840
|
+
text = messages
|
|
841
|
+
.select { |m| m.is_a?(ClaudeAgent::AssistantMessage) }
|
|
842
|
+
.map(&:text)
|
|
843
|
+
.join
|
|
844
|
+
|
|
845
|
+
# Stream deltas
|
|
846
|
+
messages.each do |msg|
|
|
847
|
+
case msg
|
|
848
|
+
when ClaudeAgent::StreamEvent
|
|
849
|
+
print msg.delta_text if msg.delta_text
|
|
850
|
+
when ClaudeAgent::ResultMessage
|
|
851
|
+
puts "\nDone (#{msg.duration_ms}ms, $#{msg.total_cost_usd})"
|
|
852
|
+
end
|
|
853
|
+
end
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
### Using text_content universally
|
|
857
|
+
|
|
858
|
+
```ruby
|
|
859
|
+
messages.each do |msg|
|
|
860
|
+
text = msg.text_content
|
|
861
|
+
puts text unless text.empty?
|
|
862
|
+
end
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### Checking message identity
|
|
866
|
+
|
|
867
|
+
```ruby
|
|
868
|
+
messages.select(&:session_message?).each do |msg|
|
|
869
|
+
puts "#{msg.type} [#{msg.uuid}] session=#{msg.session_id}"
|
|
870
|
+
end
|
|
871
|
+
```
|