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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1029f92c20c52b0d86d2942e3bb681fe0b0810ee14e5c848dc3f918a60969bb
4
- data.tar.gz: 13b11ee08fd011a9787a404b389c0c1290619eeec15abd4d7bde3885235daf61
3
+ metadata.gz: 965fad355090a487a22e76e097ed84aa7426fe4db396a28e0165e6ceaa09b1e2
4
+ data.tar.gz: fe03eeffb6dfa608c5a40bc55d1b04b31f30bd0ec5d8a50322b2dc7bf127bdea
5
5
  SHA512:
6
- metadata.gz: c7a0c0d980c96d6ebb4875916b15a7a9c49d6e6f657021722b54794951c9cb5cabad0589b4a7120210390aa0a9b27d4238e902bf6d26e141489a667bac8c9efa
7
- data.tar.gz: 72e41cc15471827325a75f67de90bef61fe0b42ebedd36dd5b88a6c942d5fcba51caafbb83005a6eea560baa2be73b4b38bc765026c58f287e1d802c61e09c57
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: ->(text) { print text },
170
- on_stream: ->(text) { print text }, # Alias for on_text
171
- on_thinking: ->(thought) { puts "Thinking: #{thought}" },
172
- on_tool_use: ->(tool) { puts "Tool: #{tool.display_label}" },
173
- on_tool_result: ->(result) { puts "Result: #{result.content&.slice(0, 80)}" },
174
- on_result: ->(result) { puts "Done! Cost: $#{result.total_cost_usd}" },
175
- on_message: ->(msg) { log(msg) } # Catch-all
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.61 (npm package)
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-26
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` | ❌ | | ✅ | Message parsing failed |
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` for real-time background agent progress reporting
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 update (no new SDK-facing features)
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.61
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)
@@ -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
- # @param event [Symbol] Event name (:message, :text, :thinking, :tool_use, :tool_result, :result)
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
- # @!method on_text(&block)
176
- # Register a handler for assistant text content
177
- # @yield [String] Text from the AssistantMessage
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 forwards to Options
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
- attr_reader :turns, :messages, :tool_activity, :client
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.slice(*CONVERSATION_KEYS)
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(conversation_kwargs)
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(kwargs)
205
- mapping = {
206
- on_text: :text, on_stream: :text, on_thinking: :thinking,
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
- mapping.each do |key, event|
212
- callback = kwargs[key]
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