claude_agent 0.7.10 → 0.7.11
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 +25 -0
- data/README.md +72 -29
- data/SPEC.md +79 -12
- data/lib/claude_agent/client.rb +10 -32
- data/lib/claude_agent/control_protocol.rb +13 -3
- data/lib/claude_agent/conversation.rb +38 -20
- data/lib/claude_agent/event_handler.rb +71 -22
- data/lib/claude_agent/hooks.rb +2 -2
- data/lib/claude_agent/live_tool_activity.rb +136 -0
- data/lib/claude_agent/message_parser.rb +16 -3
- data/lib/claude_agent/messages.rb +15 -5
- data/lib/claude_agent/options.rb +3 -23
- data/lib/claude_agent/query.rb +0 -54
- data/lib/claude_agent/tool_activity_tracker.rb +176 -0
- data/lib/claude_agent/types.rb +16 -5
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +2 -0
- data/sig/claude_agent.rbs +137 -12
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 965fad355090a487a22e76e097ed84aa7426fe4db396a28e0165e6ceaa09b1e2
|
|
4
|
+
data.tar.gz: fe03eeffb6dfa608c5a40bc55d1b04b31f30bd0ec5d8a50322b2dc7bf127bdea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b7353c7235415c842e29ba73c315f10ba52d6731324d0e326ecfe56f321272246a6877f07fd2e451007b3ac9252c007f312da15650c77f8ef4c99d06a1e907d1
|
|
7
|
+
data.tar.gz: ece71bf88dddd47d40da3dd13e660c8e39eba12484daaf1fa0b87731ff130b8b6ddfcb215e352d2f34c587eb55998a9d1669bbe88363a2587e342190c5b80c0e
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.11] - 2026-02-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `LiveToolActivity` — mutable, real-time tool status tracker (`:running` → `:done`/`:error`) with elapsed time and delegation to `ToolUseBlock`
|
|
14
|
+
- `ToolActivityTracker` — `Enumerable` collection that auto-wires to `EventHandler` or `Client` via `.attach`, with `on_start`/`on_complete`/`on_progress` callbacks, `on_change` catch-all, `running`/`done`/`errored` filtered views, and `reset!`
|
|
15
|
+
- `Conversation` accepts `track_tools: true` to opt into live tool tracking via `tool_tracker` accessor
|
|
16
|
+
- Convention-based event dispatch — every message type now auto-fires a dedicated event based on `message.type` (e.g. `:assistant`, `:stream_event`, `:status`, `:tool_progress`)
|
|
17
|
+
- `EventHandler::EVENTS`, `TYPE_EVENTS`, `DECOMPOSED_EVENTS`, `META_EVENTS` constants enumerating all known events
|
|
18
|
+
- `on_assistant`, `on_user`, `on_stream_event`, `on_status`, `on_tool_progress`, `on_hook_response`, `on_auth_status`, `on_task_notification`, `on_hook_started`, `on_hook_progress`, `on_tool_use_summary`, `on_task_started`, `on_task_progress`, `on_rate_limit_event`, `on_prompt_suggestion`, `on_files_persisted` convenience methods on `EventHandler` and `Client`
|
|
19
|
+
- `Conversation` now accepts any `on_*` keyword argument as a callback (pattern-based, no longer limited to a hardcoded list)
|
|
20
|
+
- `GenericMessage` fires its dynamic type symbol, so `on(:fancy_new_type)` works for future/unknown CLI message types
|
|
21
|
+
- `TaskNotificationMessage#tool_use_id` and `#usage` fields with new `TaskUsage` type (TypeScript SDK parity)
|
|
22
|
+
- `ToolProgressMessage#task_id` field (TypeScript SDK parity)
|
|
23
|
+
- `StatusMessage#permission_mode` field (TypeScript SDK parity)
|
|
24
|
+
- `ModelInfo#supports_effort`, `#supported_effort_levels`, `#supports_adaptive_thinking` fields (TypeScript SDK parity)
|
|
25
|
+
- `McpServerStatus#error`, `#config`, `#scope`, `#tools` fields (TypeScript SDK parity)
|
|
26
|
+
- `StopInput#last_assistant_message` hook field (TypeScript SDK parity)
|
|
27
|
+
- `SubagentStopInput#agent_type` and `#last_assistant_message` hook fields (TypeScript SDK parity)
|
|
28
|
+
- `'oauth'` source in `API_KEY_SOURCES` constant (TypeScript SDK parity)
|
|
29
|
+
- RBS signatures for all new fields
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
- `EventHandler#handle` now fires three layers per message: `:message` (catch-all) → `message.type` (type-based) → decomposed (`:text`, `:thinking`, `:tool_use`, `:tool_result`)
|
|
33
|
+
- `Conversation::CONVERSATION_KEYS` now contains only infrastructure keys (`client`, `options`, `on_permission`); callback detection is pattern-based via `on_*` prefix
|
|
34
|
+
|
|
10
35
|
## [0.7.10] - 2026-02-26
|
|
11
36
|
|
|
12
37
|
### Added
|
data/README.md
CHANGED
|
@@ -93,26 +93,6 @@ ClaudeAgent::Client.open do |client|
|
|
|
93
93
|
end
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
### Run Setup Hooks
|
|
97
|
-
|
|
98
|
-
Run Setup hooks without starting a conversation (useful for CI/CD pipelines):
|
|
99
|
-
|
|
100
|
-
```ruby
|
|
101
|
-
require "claude_agent"
|
|
102
|
-
|
|
103
|
-
# Run init Setup hooks (default)
|
|
104
|
-
messages = ClaudeAgent.run_setup
|
|
105
|
-
result = messages.last
|
|
106
|
-
puts "Setup completed" if result.success?
|
|
107
|
-
|
|
108
|
-
# Run init Setup hooks with custom options
|
|
109
|
-
options = ClaudeAgent::Options.new(cwd: "/my/project")
|
|
110
|
-
ClaudeAgent.run_setup(trigger: :init, options: options)
|
|
111
|
-
|
|
112
|
-
# Run maintenance Setup hooks
|
|
113
|
-
ClaudeAgent.run_setup(trigger: :maintenance)
|
|
114
|
-
```
|
|
115
|
-
|
|
116
96
|
## Conversation API
|
|
117
97
|
|
|
118
98
|
The `Conversation` class manages the full lifecycle: auto-connects on first message, tracks multi-turn history, accumulates usage, and builds a unified tool activity timeline.
|
|
@@ -162,17 +142,20 @@ conversation.close
|
|
|
162
142
|
|
|
163
143
|
### Callbacks
|
|
164
144
|
|
|
165
|
-
Register callbacks for real-time event handling
|
|
145
|
+
Register callbacks for real-time event handling. Any `on_*` keyword is accepted — see [Event Handlers](#event-handlers) for the full list.
|
|
166
146
|
|
|
167
147
|
```ruby
|
|
168
148
|
conversation = ClaudeAgent.conversation(
|
|
169
|
-
on_text:
|
|
170
|
-
on_stream:
|
|
171
|
-
on_thinking:
|
|
172
|
-
on_tool_use:
|
|
173
|
-
on_tool_result:
|
|
174
|
-
on_result:
|
|
175
|
-
on_message:
|
|
149
|
+
on_text: ->(text) { print text },
|
|
150
|
+
on_stream: ->(text) { print text }, # Alias for on_text
|
|
151
|
+
on_thinking: ->(thought) { puts "Thinking: #{thought}" },
|
|
152
|
+
on_tool_use: ->(tool) { puts "Tool: #{tool.display_label}" },
|
|
153
|
+
on_tool_result: ->(result) { puts "Result: #{result.content&.slice(0, 80)}" },
|
|
154
|
+
on_result: ->(result) { puts "Done! Cost: $#{result.total_cost_usd}" },
|
|
155
|
+
on_message: ->(msg) { log(msg) }, # Catch-all
|
|
156
|
+
on_stream_event: ->(evt) { handle_stream(evt) }, # Type-based
|
|
157
|
+
on_status: ->(status) { show_status(status) },
|
|
158
|
+
on_tool_progress: ->(prog) { update_spinner(prog) }
|
|
176
159
|
)
|
|
177
160
|
```
|
|
178
161
|
|
|
@@ -192,12 +175,49 @@ ClaudeAgent::Conversation.open(permission_mode: "acceptEdits") do |c|
|
|
|
192
175
|
end
|
|
193
176
|
```
|
|
194
177
|
|
|
178
|
+
### Live Tool Tracking
|
|
179
|
+
|
|
180
|
+
Track tool status in real time for live UIs. Unlike `tool_activity` (built after a turn), `LiveToolActivity` updates as tools run:
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
# Conversation level — opt in with track_tools: true
|
|
184
|
+
ClaudeAgent::Conversation.open(
|
|
185
|
+
permission_mode: "acceptEdits",
|
|
186
|
+
track_tools: true
|
|
187
|
+
) do |c|
|
|
188
|
+
c.tool_tracker.on_start { |entry| puts "▸ #{entry.display_label}" }
|
|
189
|
+
c.tool_tracker.on_progress { |entry| puts " #{entry.elapsed&.round(1)}s..." }
|
|
190
|
+
c.tool_tracker.on_complete { |entry| puts "✓ #{entry.display_label}" }
|
|
191
|
+
|
|
192
|
+
c.say("Fix the bug in auth.rb")
|
|
193
|
+
# Tracker resets between turns automatically
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Client level — attach directly
|
|
197
|
+
tracker = ClaudeAgent::ToolActivityTracker.attach(client)
|
|
198
|
+
tracker.on_start { |entry| show_spinner(entry) }
|
|
199
|
+
tracker.on_complete { |entry| hide_spinner(entry) }
|
|
200
|
+
|
|
201
|
+
# Standalone — attach to any EventHandler
|
|
202
|
+
tracker = ClaudeAgent::ToolActivityTracker.attach(event_handler)
|
|
203
|
+
|
|
204
|
+
# Catch-all callback (receives event symbol + entry)
|
|
205
|
+
tracker.on_change { |event, entry| log(event, entry.id) }
|
|
206
|
+
|
|
207
|
+
# Query running/completed tools at any point
|
|
208
|
+
tracker.running # => [LiveToolActivity, ...]
|
|
209
|
+
tracker.done # => [LiveToolActivity, ...]
|
|
210
|
+
tracker.errored # => [LiveToolActivity, ...]
|
|
211
|
+
tracker["toolu_01ABC"] # => LiveToolActivity (O(1) lookup by tool use ID)
|
|
212
|
+
```
|
|
213
|
+
|
|
195
214
|
### Conversation Accessors
|
|
196
215
|
|
|
197
216
|
```ruby
|
|
198
217
|
conversation.turns # Array of TurnResult objects
|
|
199
218
|
conversation.messages # All messages across all turns
|
|
200
219
|
conversation.tool_activity # Array of ToolActivity objects
|
|
220
|
+
conversation.tool_tracker # ToolActivityTracker (when track_tools: true)
|
|
201
221
|
conversation.total_cost # Total cost in USD
|
|
202
222
|
conversation.session_id # Session ID from most recent turn
|
|
203
223
|
conversation.usage # CumulativeUsage stats
|
|
@@ -390,15 +410,27 @@ turn.content_blocks # All content blocks across assistant messages
|
|
|
390
410
|
|
|
391
411
|
Register typed callbacks instead of writing `case` statements. Works with `Client`, `Conversation`, or standalone.
|
|
392
412
|
|
|
413
|
+
Three event layers fire for every message:
|
|
414
|
+
|
|
415
|
+
1. **Catch-all** — `:message` fires for every message
|
|
416
|
+
2. **Type-based** — `message.type` fires (e.g. `:assistant`, `:stream_event`, `:status`, `:tool_progress`)
|
|
417
|
+
3. **Decomposed** — convenience events for rich content (`:text`, `:thinking`, `:tool_use`, `:tool_result`)
|
|
418
|
+
|
|
393
419
|
### Via Client
|
|
394
420
|
|
|
395
421
|
```ruby
|
|
396
422
|
ClaudeAgent::Client.open do |client|
|
|
423
|
+
# Decomposed events (extracted content)
|
|
397
424
|
client.on_text { |text| print text }
|
|
398
425
|
client.on_tool_use { |tool| puts "\nUsing: #{tool.display_label}" }
|
|
399
426
|
client.on_tool_result { |result, tool_use| puts "Done: #{tool_use&.name}" }
|
|
400
427
|
client.on_result { |result| puts "\nCost: $#{result.total_cost_usd}" }
|
|
401
428
|
|
|
429
|
+
# Type-based events (full message object)
|
|
430
|
+
client.on_stream_event { |evt| handle_stream(evt) }
|
|
431
|
+
client.on_status { |status| show_status(status) }
|
|
432
|
+
client.on_tool_progress { |prog| update_spinner(prog) }
|
|
433
|
+
|
|
402
434
|
client.send_and_receive("Fix the bug in auth.rb")
|
|
403
435
|
end
|
|
404
436
|
```
|
|
@@ -424,6 +456,10 @@ handler.on(:tool_result) { |result, tool_use| puts "Result for #{tool_use&.name}
|
|
|
424
456
|
handler.on(:result) { |result| puts "Cost: $#{result.total_cost_usd}" }
|
|
425
457
|
handler.on(:message) { |msg| log(msg) } # Catch-all
|
|
426
458
|
|
|
459
|
+
# Type-based events work with on() too
|
|
460
|
+
handler.on(:stream_event) { |evt| handle_stream(evt) }
|
|
461
|
+
handler.on(:status) { |status| show_status(status) }
|
|
462
|
+
|
|
427
463
|
# Dispatch manually
|
|
428
464
|
client.receive_response.each { |msg| handler.handle(msg) }
|
|
429
465
|
handler.reset! # Clear turn state between turns
|
|
@@ -1133,6 +1169,11 @@ client.on_tool_result { |result, tool_use| puts "Done: #{tool_use&.name}" }
|
|
|
1133
1169
|
client.on_thinking { |thought| puts thought }
|
|
1134
1170
|
client.on_result { |result| puts "Cost: $#{result.total_cost_usd}" }
|
|
1135
1171
|
client.on_message { |msg| log(msg) }
|
|
1172
|
+
# Type-based events for all message types
|
|
1173
|
+
client.on_assistant { |msg| handle_assistant(msg) }
|
|
1174
|
+
client.on_stream_event { |evt| handle_stream(evt) }
|
|
1175
|
+
client.on_status { |status| show_status(status) }
|
|
1176
|
+
client.on_tool_progress { |prog| update_spinner(prog) }
|
|
1136
1177
|
|
|
1137
1178
|
# Control methods
|
|
1138
1179
|
client.interrupt # Cancel current operation
|
|
@@ -1349,7 +1390,9 @@ session = ClaudeAgent.unstable_v2_create_session(options)
|
|
|
1349
1390
|
| Type | Purpose |
|
|
1350
1391
|
|--------------------------|----------------------------------------------------------------------------------|
|
|
1351
1392
|
| `TurnResult` | Complete agent turn with text, tools, usage, and status accessors |
|
|
1352
|
-
| `ToolActivity` | Tool use/result pair with turn index and timing
|
|
1393
|
+
| `ToolActivity` | Tool use/result pair with turn index and timing (immutable, post-turn) |
|
|
1394
|
+
| `LiveToolActivity` | Mutable real-time tool status (running/done/error) with elapsed time |
|
|
1395
|
+
| `ToolActivityTracker` | Enumerable collection of `LiveToolActivity` with auto-wiring and `on_change` |
|
|
1353
1396
|
| `CumulativeUsage` | Running totals of tokens, cost, turns, and duration |
|
|
1354
1397
|
| `PermissionRequest` | Deferred permission promise resolvable from any thread |
|
|
1355
1398
|
| `PermissionQueue` | Thread-safe queue of pending permission requests |
|
data/SPEC.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
This document provides a comprehensive specification of the Claude Agent SDK, comparing feature parity across the official TypeScript and Python SDKs with this Ruby implementation.
|
|
4
4
|
|
|
5
5
|
**Reference Versions:**
|
|
6
|
-
- TypeScript SDK: v0.2.
|
|
6
|
+
- TypeScript SDK: v0.2.62 (npm package)
|
|
7
7
|
- Python SDK: v0.1.44 from GitHub (commit 7297bdc)
|
|
8
8
|
- Ruby SDK: This repository
|
|
9
9
|
|
|
10
|
-
**Last Updated:** 2026-02-
|
|
10
|
+
**Last Updated:** 2026-02-27
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
@@ -68,7 +68,6 @@ Configuration options for SDK queries and clients.
|
|
|
68
68
|
| `additionalDirectories` | ✅ | ✅ | ✅ | Extra allowed directories |
|
|
69
69
|
| `env` | ✅ | ✅ | ✅ | Environment variables |
|
|
70
70
|
| `sandbox` | ✅ | ✅ | ✅ | Sandbox settings |
|
|
71
|
-
| `settings` | ✅ | ✅ | ✅ | Settings file path or JSON string (e.g., plansDirectory) |
|
|
72
71
|
| `settingSources` | ✅ | ✅ | ✅ | Which settings to load |
|
|
73
72
|
| `plugins` | ✅ | ✅ | ✅ | Plugin configurations |
|
|
74
73
|
| `betas` | ✅ | ✅ | ✅ | Beta features (e.g., context-1m-2025-08-07) |
|
|
@@ -79,10 +78,6 @@ Configuration options for SDK queries and clients.
|
|
|
79
78
|
| `executable` | ✅ | N/A | N/A | JS runtime (node/bun/deno) - JS-specific |
|
|
80
79
|
| `executableArgs` | ✅ | N/A | N/A | JS runtime args - JS-specific |
|
|
81
80
|
| `extraArgs` | ✅ | ✅ | ✅ | Extra CLI arguments |
|
|
82
|
-
| `user` | ✅ | ✅ | ✅ | User identifier (V2 Session API) |
|
|
83
|
-
| `init` | ✅ | ❌ | ✅ | Run Setup hooks (init trigger), then continue (hidden CLI) |
|
|
84
|
-
| `initOnly` | ✅ | ❌ | ✅ | Run Setup hooks (init trigger), then exit (hidden CLI) |
|
|
85
|
-
| `maintenance` | ✅ | ❌ | ✅ | Run Setup hooks (maintenance trigger), continue (hidden CLI) |
|
|
86
81
|
| `promptSuggestions` | ✅ | ❌ | ✅ | Enable prompt suggestion after each turn (v0.2.47) |
|
|
87
82
|
| `debug` | ✅ | ❌ | ✅ | Enable verbose debug logging |
|
|
88
83
|
| `debugFile` | ✅ | ❌ | ✅ | Write debug logs to specific file path |
|
|
@@ -148,6 +143,34 @@ Messages exchanged between SDK and CLI.
|
|
|
148
143
|
| `error_max_budget_usd` | ✅ | ✅ | ✅ | Budget exceeded |
|
|
149
144
|
| `error_max_structured_output_retries` | ✅ | ❌ | ✅ | Structured output retries exceeded |
|
|
150
145
|
|
|
146
|
+
#### TaskNotificationMessage
|
|
147
|
+
|
|
148
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
149
|
+
|---------------|:----------:|:------:|:----:|-----------------------------|
|
|
150
|
+
| `task_id` | ✅ | ❌ | ✅ | Task identifier |
|
|
151
|
+
| `tool_use_id` | ✅ | ❌ | ✅ | Correlating tool call ID |
|
|
152
|
+
| `status` | ✅ | ❌ | ✅ | completed/failed/stopped |
|
|
153
|
+
| `output_file` | ✅ | ❌ | ✅ | Path to task output |
|
|
154
|
+
| `summary` | ✅ | ❌ | ✅ | Task summary |
|
|
155
|
+
| `usage` | ✅ | ❌ | ✅ | Tokens/tool counts/duration |
|
|
156
|
+
|
|
157
|
+
#### ToolProgressMessage
|
|
158
|
+
|
|
159
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
160
|
+
|------------------------|:----------:|:------:|:----:|-------------------------|
|
|
161
|
+
| `tool_use_id` | ✅ | ❌ | ✅ | Tool use ID |
|
|
162
|
+
| `tool_name` | ✅ | ❌ | ✅ | Tool name |
|
|
163
|
+
| `parent_tool_use_id` | ✅ | ❌ | ✅ | Parent tool use ID |
|
|
164
|
+
| `elapsed_time_seconds` | ✅ | ❌ | ✅ | Elapsed time |
|
|
165
|
+
| `task_id` | ✅ | ❌ | ✅ | Associated task ID |
|
|
166
|
+
|
|
167
|
+
#### StatusMessage
|
|
168
|
+
|
|
169
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
170
|
+
|------------------|:----------:|:------:|:----:|-------------------------|
|
|
171
|
+
| `status` | ✅ | ❌ | ✅ | Current status |
|
|
172
|
+
| `permissionMode` | ✅ | ❌ | ✅ | Current permission mode |
|
|
173
|
+
|
|
151
174
|
#### HookResponseMessage Fields
|
|
152
175
|
|
|
153
176
|
| Field | TypeScript | Python | Ruby | Notes |
|
|
@@ -252,6 +275,29 @@ Bidirectional control protocol for SDK-CLI communication.
|
|
|
252
275
|
| `McpSetServersResult` | ✅ | ❌ | ✅ | Set servers result |
|
|
253
276
|
| `RewindFilesResult` | ✅ | ✅ | ✅ | Rewind result |
|
|
254
277
|
|
|
278
|
+
#### ModelInfo Fields
|
|
279
|
+
|
|
280
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
281
|
+
|----------------------------|:----------:|:------:|:----:|---------------------------------|
|
|
282
|
+
| `value` | ✅ | ❌ | ✅ | Model identifier |
|
|
283
|
+
| `displayName` | ✅ | ❌ | ✅ | Human-readable name |
|
|
284
|
+
| `description` | ✅ | ❌ | ✅ | Model description |
|
|
285
|
+
| `supportsEffort` | ✅ | ❌ | ✅ | Whether model supports effort |
|
|
286
|
+
| `supportedEffortLevels` | ✅ | ❌ | ✅ | Available effort levels |
|
|
287
|
+
| `supportsAdaptiveThinking` | ✅ | ❌ | ✅ | Whether adaptive thinking works |
|
|
288
|
+
|
|
289
|
+
#### McpServerStatus Fields
|
|
290
|
+
|
|
291
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
292
|
+
|--------------|:----------:|:------:|:----:|------------------------------------|
|
|
293
|
+
| `name` | ✅ | ✅ | ✅ | Server name |
|
|
294
|
+
| `status` | ✅ | ✅ | ✅ | Connection status |
|
|
295
|
+
| `serverInfo` | ✅ | ❌ | ✅ | Server name/version |
|
|
296
|
+
| `error` | ✅ | ❌ | ✅ | Error message (when failed) |
|
|
297
|
+
| `config` | ✅ | ❌ | ✅ | Server configuration |
|
|
298
|
+
| `scope` | ✅ | ❌ | ✅ | Config scope (project, user, etc.) |
|
|
299
|
+
| `tools` | ✅ | ❌ | ✅ | Tools with annotations |
|
|
300
|
+
|
|
255
301
|
---
|
|
256
302
|
|
|
257
303
|
## 5. Hooks
|
|
@@ -304,6 +350,23 @@ Event hooks for intercepting and modifying SDK behavior.
|
|
|
304
350
|
| `WorktreeCreateHookInput` | ✅ | ❌ | ✅ |
|
|
305
351
|
| `WorktreeRemoveHookInput` | ✅ | ❌ | ✅ |
|
|
306
352
|
|
|
353
|
+
#### StopHookInput Fields
|
|
354
|
+
|
|
355
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
356
|
+
|--------------------------|:----------:|:------:|:----:|----------------------------------------|
|
|
357
|
+
| `stop_hook_active` | ✅ | ✅ | ✅ | Whether stop hook is active |
|
|
358
|
+
| `last_assistant_message` | ✅ | ❌ | ✅ | Last assistant message text (v0.2.51+) |
|
|
359
|
+
|
|
360
|
+
#### SubagentStopHookInput Fields
|
|
361
|
+
|
|
362
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
363
|
+
|--------------------------|:----------:|:------:|:----:|----------------------------------------|
|
|
364
|
+
| `stop_hook_active` | ✅ | ✅ | ✅ | Whether stop hook is active |
|
|
365
|
+
| `agent_id` | ✅ | ✅ | ✅ | Subagent identifier |
|
|
366
|
+
| `agent_transcript_path` | ✅ | ✅ | ✅ | Path to agent transcript |
|
|
367
|
+
| `agent_type` | ✅ | ✅ | ✅ | Agent type |
|
|
368
|
+
| `last_assistant_message` | ✅ | ❌ | ✅ | Last assistant message text (v0.2.51+) |
|
|
369
|
+
|
|
307
370
|
### Hook Output Types
|
|
308
371
|
|
|
309
372
|
| Output Field | TypeScript | Python | Ruby | Notes |
|
|
@@ -651,7 +714,7 @@ Error types and hierarchy.
|
|
|
651
714
|
| `CLIConnectionError` | ❌ | ✅ | ✅ | Connection failed |
|
|
652
715
|
| `ProcessError` | ❌ | ✅ | ✅ | CLI process failed |
|
|
653
716
|
| `JSONDecodeError` | ❌ | ✅ | ✅ | JSON parsing failed |
|
|
654
|
-
| `MessageParseError` | ❌ |
|
|
717
|
+
| `MessageParseError` | ❌ | ✅ | ✅ | Message parsing failed |
|
|
655
718
|
| `TimeoutError` | ❌ | ❌ | ✅ | Control request timeout |
|
|
656
719
|
| `ConfigurationError` | ❌ | ❌ | ✅ | Invalid configuration |
|
|
657
720
|
|
|
@@ -747,25 +810,28 @@ Public API surface for SDK clients.
|
|
|
747
810
|
- Source is bundled/minified, but `sdk.d.ts` provides complete type definitions
|
|
748
811
|
- Includes unstable V2 session API
|
|
749
812
|
- `executable`/`executableArgs` are JS-specific (`node`/`bun`/`deno`)
|
|
813
|
+
- Does NOT have `settings`, `user`, `init`/`initOnly`/`maintenance` as typed Options (use `extraArgs` or `settingSources`)
|
|
814
|
+
- `ApiKeySource` includes `'oauth'`
|
|
750
815
|
- v0.2.45: Added `TaskStartedMessage`, `RateLimitEvent` message types
|
|
751
816
|
- v0.2.47: Added `promptSuggestions` option and `PromptSuggestionMessage`
|
|
752
817
|
- v0.2.49: Added `ConfigChange` hook event, `SandboxFilesystemConfig`, ModelInfo capability fields
|
|
753
818
|
- v0.2.50: Added `WorktreeCreate`/`WorktreeRemove` hook events, `apply_flag_settings` control request
|
|
754
|
-
- v0.2.51: Added `TaskProgressMessage`
|
|
819
|
+
- v0.2.51: Added `TaskProgressMessage`, `StopHookInput.last_assistant_message`, `SubagentStopHookInput.last_assistant_message`
|
|
755
820
|
- v0.2.52: Added `mcp_authenticate`/`mcp_clear_auth` control requests for MCP server authentication
|
|
756
821
|
- v0.2.53: Added `listSessions()` for discovering and listing past sessions with `SDKSessionInfo` metadata
|
|
757
822
|
- v0.2.54 – v0.2.58: CLI parity updates (no new SDK-facing features)
|
|
758
823
|
- v0.2.59: Added `getSessionMessages()` for reading session transcript history with pagination (limit/offset)
|
|
759
|
-
- v0.2.61: CLI parity
|
|
824
|
+
- v0.2.61 – v0.2.62: CLI parity updates (no new SDK-facing features)
|
|
760
825
|
|
|
761
826
|
### Python SDK
|
|
762
827
|
- Full source available with `Transport` abstract class
|
|
763
828
|
- Partial control protocol: query and client support interrupt, setPermissionMode, setModel, rewindFiles, mcpStatus
|
|
764
|
-
- Has `CLINotFoundError`, `CLIConnectionError`, `ProcessError`, `CLIJSONDecodeError` error types
|
|
829
|
+
- Has `CLINotFoundError`, `CLIConnectionError`, `ProcessError`, `CLIJSONDecodeError`, `MessageParseError` error types
|
|
765
830
|
- Missing hooks: SessionStart, SessionEnd, Setup, TeammateIdle, TaskCompleted, ConfigChange, WorktreeCreate, WorktreeRemove
|
|
766
831
|
- Missing permission modes: `dontAsk`
|
|
767
832
|
- Missing options: `allowDangerouslySkipPermissions`, `persistSession`, `resumeSessionAt`, `sessionId`, `strictMcpConfig`, `init`/`initOnly`/`maintenance`, `debug`/`debugFile`, `promptSuggestions`
|
|
768
833
|
- `ToolPermissionContext` missing `blockedPath`, `decisionReason`, `toolUseID`, `agentID`, `description`
|
|
834
|
+
- Has `agent_type` field in `SubagentStopHookInput`
|
|
769
835
|
- Has SDK MCP server support with `tool()` helper and annotations
|
|
770
836
|
- Added `thinking` config and `effort` option in v0.1.36
|
|
771
837
|
- Handles `rate_limit_event` and unknown message types gracefully (v0.1.40)
|
|
@@ -773,8 +839,9 @@ Public API surface for SDK clients.
|
|
|
773
839
|
- v0.1.42 – v0.1.44: CLI parity updates (no new SDK-facing features; latest commit bumps bundled CLI to v2.1.61)
|
|
774
840
|
|
|
775
841
|
### Ruby SDK (This Repository)
|
|
776
|
-
- Feature parity with TypeScript SDK v0.2.
|
|
842
|
+
- Feature parity with TypeScript SDK v0.2.62
|
|
777
843
|
- Ruby-idiomatic patterns (Data.define, snake_case)
|
|
778
844
|
- Complete control protocol, hook, and V2 Session API support
|
|
779
845
|
- Dedicated Client class for multi-turn conversations
|
|
780
846
|
- `executable`/`executableArgs` marked N/A (JS runtime options)
|
|
847
|
+
- Has `settings`, `init`/`initOnly`/`maintenance`, `user` options (not typed in TS SDK)
|
data/lib/claude_agent/client.rb
CHANGED
|
@@ -159,50 +159,28 @@ module ClaudeAgent
|
|
|
159
159
|
# Handlers persist across turns and fire automatically during
|
|
160
160
|
# {#receive_turn} and {#send_and_receive}.
|
|
161
161
|
#
|
|
162
|
-
#
|
|
162
|
+
# See {EventHandler} for the full event hierarchy:
|
|
163
|
+
# - +:message+ — catch-all for every message
|
|
164
|
+
# - Type-based — +:assistant+, +:stream_event+, +:status+, etc.
|
|
165
|
+
# - Decomposed — +:text+, +:thinking+, +:tool_use+, +:tool_result+
|
|
166
|
+
#
|
|
167
|
+
# @param event [Symbol] Event name (see {EventHandler::EVENTS})
|
|
163
168
|
# @yield Event-specific arguments
|
|
164
169
|
# @return [self]
|
|
165
170
|
#
|
|
166
171
|
# @example
|
|
167
172
|
# client.on(:text) { |text| print text }
|
|
168
173
|
# client.on(:tool_use) { |tool| show_spinner(tool) }
|
|
174
|
+
# client.on(:stream_event) { |evt| handle_stream(evt) }
|
|
169
175
|
#
|
|
170
176
|
def on(event, &block)
|
|
171
177
|
@event_handler.on(event, &block)
|
|
172
178
|
self
|
|
173
179
|
end
|
|
174
180
|
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
|
|
178
|
-
# @return [self]
|
|
179
|
-
|
|
180
|
-
# @!method on_thinking(&block)
|
|
181
|
-
# Register a handler for assistant thinking content
|
|
182
|
-
# @yield [String] Thinking from the AssistantMessage
|
|
183
|
-
# @return [self]
|
|
184
|
-
|
|
185
|
-
# @!method on_tool_use(&block)
|
|
186
|
-
# Register a handler for tool use requests
|
|
187
|
-
# @yield [ToolUseBlock, ServerToolUseBlock] The tool use block
|
|
188
|
-
# @return [self]
|
|
189
|
-
|
|
190
|
-
# @!method on_tool_result(&block)
|
|
191
|
-
# Register a handler for tool results, paired with the original request
|
|
192
|
-
# @yield [ToolResultBlock, ToolUseBlock|nil] Result block and matched tool use
|
|
193
|
-
# @return [self]
|
|
194
|
-
|
|
195
|
-
# @!method on_result(&block)
|
|
196
|
-
# Register a handler for the final ResultMessage
|
|
197
|
-
# @yield [ResultMessage] The result
|
|
198
|
-
# @return [self]
|
|
199
|
-
|
|
200
|
-
# @!method on_message(&block)
|
|
201
|
-
# Register a handler for every message (catch-all)
|
|
202
|
-
# @yield [message] Any message object
|
|
203
|
-
# @return [self]
|
|
204
|
-
|
|
205
|
-
%i[message text thinking tool_use tool_result result].each do |event|
|
|
181
|
+
# Generates on_* convenience methods for all known events.
|
|
182
|
+
# See {EventHandler::EVENTS} for the complete list.
|
|
183
|
+
EventHandler::EVENTS.each do |event|
|
|
206
184
|
define_method(:"on_#{event}") { |&block| on(event, &block) }
|
|
207
185
|
end
|
|
208
186
|
|
|
@@ -352,7 +352,10 @@ module ClaudeAgent
|
|
|
352
352
|
ModelInfo.new(
|
|
353
353
|
value: model["value"],
|
|
354
354
|
display_name: model["displayName"],
|
|
355
|
-
description: model["description"]
|
|
355
|
+
description: model["description"],
|
|
356
|
+
supports_effort: model["supportsEffort"],
|
|
357
|
+
supported_effort_levels: model["supportedEffortLevels"],
|
|
358
|
+
supports_adaptive_thinking: model["supportsAdaptiveThinking"]
|
|
356
359
|
)
|
|
357
360
|
end
|
|
358
361
|
|
|
@@ -384,7 +387,10 @@ module ClaudeAgent
|
|
|
384
387
|
ModelInfo.new(
|
|
385
388
|
value: model["value"],
|
|
386
389
|
display_name: model["displayName"],
|
|
387
|
-
description: model["description"]
|
|
390
|
+
description: model["description"],
|
|
391
|
+
supports_effort: model["supportsEffort"],
|
|
392
|
+
supported_effort_levels: model["supportedEffortLevels"],
|
|
393
|
+
supports_adaptive_thinking: model["supportsAdaptiveThinking"]
|
|
388
394
|
)
|
|
389
395
|
end
|
|
390
396
|
end
|
|
@@ -397,7 +403,11 @@ module ClaudeAgent
|
|
|
397
403
|
McpServerStatus.new(
|
|
398
404
|
name: server["name"],
|
|
399
405
|
status: server["status"],
|
|
400
|
-
server_info: server["serverInfo"]
|
|
406
|
+
server_info: server["serverInfo"],
|
|
407
|
+
error: server["error"],
|
|
408
|
+
config: server["config"],
|
|
409
|
+
scope: server["scope"],
|
|
410
|
+
tools: server["tools"]
|
|
401
411
|
)
|
|
402
412
|
end
|
|
403
413
|
end
|
|
@@ -29,14 +29,13 @@ module ClaudeAgent
|
|
|
29
29
|
# conversation.say("Continue where we left off")
|
|
30
30
|
#
|
|
31
31
|
class Conversation
|
|
32
|
-
# Keys consumed by Conversation; everything else
|
|
33
|
-
CONVERSATION_KEYS = %i[
|
|
34
|
-
on_text on_stream on_tool_use on_tool_result
|
|
35
|
-
on_thinking on_result on_message on_permission
|
|
36
|
-
client options
|
|
37
|
-
].freeze
|
|
32
|
+
# Keys consumed directly by Conversation; everything else is a callback or forwarded to Options
|
|
33
|
+
CONVERSATION_KEYS = %i[client options on_permission track_tools].freeze
|
|
38
34
|
|
|
39
|
-
|
|
35
|
+
# Callback aliases (alternative names mapping to canonical events)
|
|
36
|
+
CALLBACK_ALIASES = { on_stream: :text }.freeze
|
|
37
|
+
|
|
38
|
+
attr_reader :turns, :messages, :tool_activity, :client, :tool_tracker
|
|
40
39
|
|
|
41
40
|
# Open a conversation with automatic cleanup.
|
|
42
41
|
#
|
|
@@ -62,7 +61,8 @@ module ClaudeAgent
|
|
|
62
61
|
# Create a new conversation.
|
|
63
62
|
#
|
|
64
63
|
# Accepts all {Options} keyword arguments plus conversation-level
|
|
65
|
-
# callbacks
|
|
64
|
+
# callbacks. Any +on_*+ keyword is registered as an event callback
|
|
65
|
+
# (see {EventHandler::EVENTS} for available events).
|
|
66
66
|
#
|
|
67
67
|
# @param on_text [Proc] Handler for streaming text
|
|
68
68
|
# @param on_stream [Proc] Alias for on_text
|
|
@@ -71,13 +71,15 @@ module ClaudeAgent
|
|
|
71
71
|
# @param on_thinking [Proc] Handler for thinking events
|
|
72
72
|
# @param on_result [Proc] Handler for result events
|
|
73
73
|
# @param on_message [Proc] Handler for all messages
|
|
74
|
+
# @param on_stream_event [Proc] Handler for stream events
|
|
75
|
+
# @param on_status [Proc] Handler for status messages
|
|
76
|
+
# @param on_tool_progress [Proc] Handler for tool progress messages
|
|
74
77
|
# @param on_permission [Symbol, Proc] :queue (default) or a callable for can_use_tool
|
|
75
78
|
# @param client [Client] Pre-built client (for testing)
|
|
76
79
|
# @param options [Options] Pre-built options object
|
|
77
80
|
#
|
|
78
81
|
def initialize(**kwargs)
|
|
79
|
-
conversation_kwargs = kwargs
|
|
80
|
-
options_kwargs = kwargs.except(*CONVERSATION_KEYS)
|
|
82
|
+
callbacks, conversation_kwargs, options_kwargs = partition_kwargs(kwargs)
|
|
81
83
|
|
|
82
84
|
@options = conversation_kwargs[:options] || build_options(options_kwargs, conversation_kwargs)
|
|
83
85
|
@client = conversation_kwargs[:client] || Client.new(options: @options)
|
|
@@ -89,8 +91,9 @@ module ClaudeAgent
|
|
|
89
91
|
@tool_result_timestamps = {}
|
|
90
92
|
@connected = false
|
|
91
93
|
@closed = false
|
|
94
|
+
@tool_tracker = conversation_kwargs[:track_tools] ? ToolActivityTracker.attach(@client) : nil
|
|
92
95
|
|
|
93
|
-
register_callbacks(
|
|
96
|
+
register_callbacks(callbacks)
|
|
94
97
|
register_timing_hooks
|
|
95
98
|
end
|
|
96
99
|
|
|
@@ -113,6 +116,7 @@ module ClaudeAgent
|
|
|
113
116
|
|
|
114
117
|
@turns << turn
|
|
115
118
|
build_tool_activities(turn, @turns.size - 1)
|
|
119
|
+
@tool_tracker&.reset!
|
|
116
120
|
|
|
117
121
|
logger.info("conversation") { "Turn #{@turns.size - 1} complete (#{turn.tool_uses.size} tools, cost=$#{total_cost})" }
|
|
118
122
|
|
|
@@ -189,6 +193,24 @@ module ClaudeAgent
|
|
|
189
193
|
|
|
190
194
|
private
|
|
191
195
|
|
|
196
|
+
def partition_kwargs(kwargs)
|
|
197
|
+
callbacks = {}
|
|
198
|
+
conversation_kwargs = {}
|
|
199
|
+
options_kwargs = {}
|
|
200
|
+
|
|
201
|
+
kwargs.each do |key, value|
|
|
202
|
+
if CONVERSATION_KEYS.include?(key)
|
|
203
|
+
conversation_kwargs[key] = value
|
|
204
|
+
elsif key.to_s.start_with?("on_")
|
|
205
|
+
callbacks[key] = value
|
|
206
|
+
else
|
|
207
|
+
options_kwargs[key] = value
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
[ callbacks, conversation_kwargs, options_kwargs ]
|
|
212
|
+
end
|
|
213
|
+
|
|
192
214
|
def build_options(options_kwargs, conversation_kwargs)
|
|
193
215
|
permission = conversation_kwargs[:on_permission]
|
|
194
216
|
|
|
@@ -201,16 +223,12 @@ module ClaudeAgent
|
|
|
201
223
|
Options.new(**options_kwargs)
|
|
202
224
|
end
|
|
203
225
|
|
|
204
|
-
def register_callbacks(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
on_tool_use: :tool_use, on_tool_result: :tool_result,
|
|
208
|
-
on_result: :result, on_message: :message
|
|
209
|
-
}
|
|
226
|
+
def register_callbacks(callbacks)
|
|
227
|
+
callbacks.each do |key, callback|
|
|
228
|
+
next unless callback
|
|
210
229
|
|
|
211
|
-
|
|
212
|
-
callback
|
|
213
|
-
@client.on(event, &callback) if callback
|
|
230
|
+
event = CALLBACK_ALIASES[key] || key.to_s.delete_prefix("on_").to_sym
|
|
231
|
+
@client.on(event, &callback)
|
|
214
232
|
end
|
|
215
233
|
end
|
|
216
234
|
|