claude_agent 0.7.9 → 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 +41 -0
- data/README.md +138 -53
- data/SPEC.md +116 -20
- 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/get_session_messages.rb +236 -0
- data/lib/claude_agent/hooks.rb +2 -2
- data/lib/claude_agent/list_sessions.rb +9 -119
- 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/session.rb +71 -3
- data/lib/claude_agent/session_message_relation.rb +59 -0
- data/lib/claude_agent/session_paths.rb +120 -0
- data/lib/claude_agent/tool_activity_tracker.rb +176 -0
- data/lib/claude_agent/types.rb +32 -5
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +21 -1
- data/sig/claude_agent.rbs +204 -17
- metadata +6 -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,47 @@ 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
|
+
|
|
35
|
+
## [0.7.10] - 2026-02-26
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
- `Session.find(id, dir:)` — find a past session by UUID, returns `Session` or `nil`
|
|
39
|
+
- `Session.all` — list all past sessions as `Session` objects
|
|
40
|
+
- `Session.where(dir:, limit:)` — query sessions with optional directory and limit filters
|
|
41
|
+
- `Session#messages` — returns a chainable, `Enumerable` `SessionMessageRelation` for reading transcript messages
|
|
42
|
+
- `SessionMessageRelation#where(limit:, offset:)` — paginate messages with immutable chaining
|
|
43
|
+
- `ClaudeAgent.get_session_messages(session_id, dir:, limit:, offset:)` — read session transcripts from disk (TypeScript SDK v0.2.59 parity)
|
|
44
|
+
- `SessionMessage` type — message from a session transcript with `type`, `uuid`, `session_id`, `message`
|
|
45
|
+
- `SessionPaths` module — shared path infrastructure for session discovery (extracted from `ListSessions`)
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- Renamed `Session` (V2 multi-turn API) to `V2Session` to free the `Session` name for the finder API
|
|
49
|
+
- `unstable_v2_create_session`, `unstable_v2_resume_session`, and `unstable_v2_prompt` now return `V2Session`
|
|
50
|
+
|
|
10
51
|
## [0.7.9] - 2026-02-25
|
|
11
52
|
|
|
12
53
|
### 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
|
|
@@ -1180,14 +1221,20 @@ client.disconnect
|
|
|
1180
1221
|
|
|
1181
1222
|
## Session Discovery
|
|
1182
1223
|
|
|
1183
|
-
|
|
1224
|
+
Find and inspect past Claude Code sessions from disk without spawning a CLI subprocess.
|
|
1225
|
+
|
|
1226
|
+
### Session.find / Session.all
|
|
1184
1227
|
|
|
1185
1228
|
```ruby
|
|
1186
|
-
#
|
|
1187
|
-
|
|
1229
|
+
# Find a specific session by ID
|
|
1230
|
+
session = ClaudeAgent::Session.find("abc-123-def")
|
|
1231
|
+
session = ClaudeAgent::Session.find("abc-123-def", dir: "/my/project")
|
|
1188
1232
|
|
|
1189
|
-
#
|
|
1190
|
-
sessions = ClaudeAgent.
|
|
1233
|
+
# List all sessions (most recent first)
|
|
1234
|
+
sessions = ClaudeAgent::Session.all
|
|
1235
|
+
|
|
1236
|
+
# Filter by directory, limit results
|
|
1237
|
+
sessions = ClaudeAgent::Session.where(dir: "/path/to/project", limit: 10)
|
|
1191
1238
|
|
|
1192
1239
|
sessions.each do |s|
|
|
1193
1240
|
puts "#{s.summary} (#{s.git_branch || 'no branch'})"
|
|
@@ -1197,7 +1244,27 @@ sessions.each do |s|
|
|
|
1197
1244
|
end
|
|
1198
1245
|
```
|
|
1199
1246
|
|
|
1200
|
-
|
|
1247
|
+
### Reading Messages
|
|
1248
|
+
|
|
1249
|
+
`Session#messages` returns a chainable, `Enumerable` relation:
|
|
1250
|
+
|
|
1251
|
+
```ruby
|
|
1252
|
+
session = ClaudeAgent::Session.find("abc-123-def")
|
|
1253
|
+
|
|
1254
|
+
# All messages
|
|
1255
|
+
session.messages.each { |m| puts "#{m.type}: #{m.uuid}" }
|
|
1256
|
+
|
|
1257
|
+
# Paginated
|
|
1258
|
+
session.messages.where(limit: 10).map(&:uuid)
|
|
1259
|
+
session.messages.where(offset: 5, limit: 10).to_a
|
|
1260
|
+
|
|
1261
|
+
# Enumerable methods work
|
|
1262
|
+
session.messages.first
|
|
1263
|
+
session.messages.count
|
|
1264
|
+
session.messages.select { |m| m.type == "assistant" }
|
|
1265
|
+
```
|
|
1266
|
+
|
|
1267
|
+
### Session Fields
|
|
1201
1268
|
|
|
1202
1269
|
| Field | Type | Description |
|
|
1203
1270
|
|------------------|-----------------|--------------------------------------------------|
|
|
@@ -1210,11 +1277,24 @@ Each session is a `SessionInfo` with these fields:
|
|
|
1210
1277
|
| `git_branch` | `String\|nil` | Git branch the session was on |
|
|
1211
1278
|
| `cwd` | `String\|nil` | Working directory of the session |
|
|
1212
1279
|
|
|
1280
|
+
### Functional API
|
|
1281
|
+
|
|
1282
|
+
The lower-level functional API is also available:
|
|
1283
|
+
|
|
1284
|
+
```ruby
|
|
1285
|
+
# List sessions (returns SessionInfo objects)
|
|
1286
|
+
infos = ClaudeAgent.list_sessions(dir: "/path/to/project", limit: 10)
|
|
1287
|
+
|
|
1288
|
+
# Read messages directly
|
|
1289
|
+
messages = ClaudeAgent.get_session_messages("abc-123-def", limit: 10, offset: 5)
|
|
1290
|
+
```
|
|
1291
|
+
|
|
1292
|
+
### Resume a Past Session
|
|
1293
|
+
|
|
1213
1294
|
Use with `Conversation.resume` to pick up where you left off:
|
|
1214
1295
|
|
|
1215
1296
|
```ruby
|
|
1216
|
-
|
|
1217
|
-
session = sessions.first
|
|
1297
|
+
session = ClaudeAgent::Session.where(dir: Dir.pwd, limit: 5).first
|
|
1218
1298
|
|
|
1219
1299
|
conversation = ClaudeAgent.resume_conversation(session.session_id)
|
|
1220
1300
|
turn = conversation.say("Continue where we left off")
|
|
@@ -1307,23 +1387,28 @@ session = ClaudeAgent.unstable_v2_create_session(options)
|
|
|
1307
1387
|
|
|
1308
1388
|
### Return Types
|
|
1309
1389
|
|
|
1310
|
-
| Type
|
|
1311
|
-
|
|
1312
|
-
| `TurnResult`
|
|
1313
|
-
| `ToolActivity`
|
|
1314
|
-
| `
|
|
1315
|
-
| `
|
|
1316
|
-
| `
|
|
1317
|
-
| `
|
|
1318
|
-
| `
|
|
1319
|
-
| `
|
|
1320
|
-
| `
|
|
1321
|
-
| `
|
|
1322
|
-
| `
|
|
1323
|
-
| `
|
|
1324
|
-
| `
|
|
1325
|
-
| `
|
|
1326
|
-
| `
|
|
1390
|
+
| Type | Purpose |
|
|
1391
|
+
|--------------------------|----------------------------------------------------------------------------------|
|
|
1392
|
+
| `TurnResult` | Complete agent turn with text, tools, usage, and status accessors |
|
|
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` |
|
|
1396
|
+
| `CumulativeUsage` | Running totals of tokens, cost, turns, and duration |
|
|
1397
|
+
| `PermissionRequest` | Deferred permission promise resolvable from any thread |
|
|
1398
|
+
| `PermissionQueue` | Thread-safe queue of pending permission requests |
|
|
1399
|
+
| `EventHandler` | Typed event callback registry |
|
|
1400
|
+
| `SlashCommand` | Available slash commands (name, description, argument_hint) |
|
|
1401
|
+
| `ModelInfo` | Available models (value, display_name, description) |
|
|
1402
|
+
| `McpServerStatus` | MCP server status (name, status, server_info) |
|
|
1403
|
+
| `AccountInfo` | Account information (email, organization, subscription_type) |
|
|
1404
|
+
| `ModelUsage` | Per-model usage stats (input_tokens, output_tokens, cost_usd) |
|
|
1405
|
+
| `McpSetServersResult` | Result of set_mcp_servers (added, removed, errors) |
|
|
1406
|
+
| `RewindFilesResult` | Result of rewind_files (can_rewind, error, files_changed, insertions, deletions) |
|
|
1407
|
+
| `Session` | Session finder with `.find`, `.all`, `#messages` (wraps SessionInfo) |
|
|
1408
|
+
| `SessionMessageRelation` | Chainable, Enumerable query object for session messages |
|
|
1409
|
+
| `SessionInfo` | Session metadata from `list_sessions` (session_id, summary, git_branch, cwd) |
|
|
1410
|
+
| `SessionMessage` | Message from a session transcript (type, uuid, session_id, message) |
|
|
1411
|
+
| `SDKPermissionDenial` | Permission denial info (tool_name, tool_use_id, tool_input) |
|
|
1327
1412
|
|
|
1328
1413
|
## Logging
|
|
1329
1414
|
|
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.
|
|
7
|
-
- Python SDK: v0.1.
|
|
6
|
+
- TypeScript SDK: v0.2.62 (npm package)
|
|
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 |
|
|
@@ -524,9 +587,35 @@ Session management and resumption.
|
|
|
524
587
|
|
|
525
588
|
### Session Discovery
|
|
526
589
|
|
|
527
|
-
| Feature
|
|
528
|
-
|
|
529
|
-
| `listSessions()`
|
|
590
|
+
| Feature | TypeScript | Python | Ruby | Notes |
|
|
591
|
+
|------------------------|:----------:|:------:|:----:|--------------------------------------------------|
|
|
592
|
+
| `listSessions()` | ✅ | ❌ | ✅ | List past sessions with metadata (v0.2.53) |
|
|
593
|
+
| `getSessionMessages()` | ✅ | ❌ | ✅ | Read session transcript messages (v0.2.59) |
|
|
594
|
+
|
|
595
|
+
#### ListSessionsOptions
|
|
596
|
+
|
|
597
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
598
|
+
|---------|:----------:|:------:|:----:|-------------------------------------------|
|
|
599
|
+
| `dir` | ✅ | ❌ | ✅ | Project directory (includes worktrees) |
|
|
600
|
+
| `limit` | ✅ | ❌ | ✅ | Maximum number of sessions to return |
|
|
601
|
+
|
|
602
|
+
#### GetSessionMessagesOptions
|
|
603
|
+
|
|
604
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
605
|
+
|----------|:----------:|:------:|:----:|----------------------------------------------|
|
|
606
|
+
| `dir` | ✅ | ❌ | ✅ | Project directory to find session in |
|
|
607
|
+
| `limit` | ✅ | ❌ | ✅ | Maximum number of messages to return |
|
|
608
|
+
| `offset` | ✅ | ❌ | ✅ | Number of messages to skip from the start |
|
|
609
|
+
|
|
610
|
+
#### SessionMessage Fields
|
|
611
|
+
|
|
612
|
+
| Field | TypeScript | Python | Ruby | Notes |
|
|
613
|
+
|----------------------|:----------:|:------:|:----:|----------------------------------|
|
|
614
|
+
| `type` | ✅ | ❌ | ✅ | 'user' or 'assistant' |
|
|
615
|
+
| `uuid` | ✅ | ❌ | ✅ | Message UUID |
|
|
616
|
+
| `session_id` | ✅ | ❌ | ✅ | Session ID |
|
|
617
|
+
| `message` | ✅ | ❌ | ✅ | Raw message content |
|
|
618
|
+
| `parent_tool_use_id` | ✅ | ❌ | ✅ | Parent tool use ID (always null) |
|
|
530
619
|
|
|
531
620
|
#### SDKSessionInfo Fields
|
|
532
621
|
|
|
@@ -625,7 +714,7 @@ Error types and hierarchy.
|
|
|
625
714
|
| `CLIConnectionError` | ❌ | ✅ | ✅ | Connection failed |
|
|
626
715
|
| `ProcessError` | ❌ | ✅ | ✅ | CLI process failed |
|
|
627
716
|
| `JSONDecodeError` | ❌ | ✅ | ✅ | JSON parsing failed |
|
|
628
|
-
| `MessageParseError` | ❌ |
|
|
717
|
+
| `MessageParseError` | ❌ | ✅ | ✅ | Message parsing failed |
|
|
629
718
|
| `TimeoutError` | ❌ | ❌ | ✅ | Control request timeout |
|
|
630
719
|
| `ConfigurationError` | ❌ | ❌ | ✅ | Invalid configuration |
|
|
631
720
|
|
|
@@ -649,9 +738,10 @@ Public API surface for SDK clients.
|
|
|
649
738
|
|
|
650
739
|
### Standalone Functions
|
|
651
740
|
|
|
652
|
-
| Feature
|
|
653
|
-
|
|
654
|
-
| `listSessions()`
|
|
741
|
+
| Feature | TypeScript | Python | Ruby | Notes |
|
|
742
|
+
|------------------------|:----------:|:------:|:----:|--------------------------------------------|
|
|
743
|
+
| `listSessions()` | ✅ | ❌ | ✅ | List past sessions with metadata (v0.2.53) |
|
|
744
|
+
| `getSessionMessages()` | ✅ | ❌ | ✅ | Read session transcript (v0.2.59) |
|
|
655
745
|
|
|
656
746
|
### Query Interface
|
|
657
747
|
|
|
@@ -720,32 +810,38 @@ Public API surface for SDK clients.
|
|
|
720
810
|
- Source is bundled/minified, but `sdk.d.ts` provides complete type definitions
|
|
721
811
|
- Includes unstable V2 session API
|
|
722
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'`
|
|
723
815
|
- v0.2.45: Added `TaskStartedMessage`, `RateLimitEvent` message types
|
|
724
816
|
- v0.2.47: Added `promptSuggestions` option and `PromptSuggestionMessage`
|
|
725
817
|
- v0.2.49: Added `ConfigChange` hook event, `SandboxFilesystemConfig`, ModelInfo capability fields
|
|
726
818
|
- v0.2.50: Added `WorktreeCreate`/`WorktreeRemove` hook events, `apply_flag_settings` control request
|
|
727
|
-
- v0.2.51: Added `TaskProgressMessage`
|
|
819
|
+
- v0.2.51: Added `TaskProgressMessage`, `StopHookInput.last_assistant_message`, `SubagentStopHookInput.last_assistant_message`
|
|
728
820
|
- v0.2.52: Added `mcp_authenticate`/`mcp_clear_auth` control requests for MCP server authentication
|
|
729
821
|
- v0.2.53: Added `listSessions()` for discovering and listing past sessions with `SDKSessionInfo` metadata
|
|
730
|
-
- v0.2.54 – v0.2.
|
|
822
|
+
- v0.2.54 – v0.2.58: CLI parity updates (no new SDK-facing features)
|
|
823
|
+
- v0.2.59: Added `getSessionMessages()` for reading session transcript history with pagination (limit/offset)
|
|
824
|
+
- v0.2.61 – v0.2.62: CLI parity updates (no new SDK-facing features)
|
|
731
825
|
|
|
732
826
|
### Python SDK
|
|
733
827
|
- Full source available with `Transport` abstract class
|
|
734
828
|
- Partial control protocol: query and client support interrupt, setPermissionMode, setModel, rewindFiles, mcpStatus
|
|
735
|
-
- Has `CLINotFoundError`, `CLIConnectionError`, `ProcessError`, `CLIJSONDecodeError` error types
|
|
829
|
+
- Has `CLINotFoundError`, `CLIConnectionError`, `ProcessError`, `CLIJSONDecodeError`, `MessageParseError` error types
|
|
736
830
|
- Missing hooks: SessionStart, SessionEnd, Setup, TeammateIdle, TaskCompleted, ConfigChange, WorktreeCreate, WorktreeRemove
|
|
737
831
|
- Missing permission modes: `dontAsk`
|
|
738
832
|
- Missing options: `allowDangerouslySkipPermissions`, `persistSession`, `resumeSessionAt`, `sessionId`, `strictMcpConfig`, `init`/`initOnly`/`maintenance`, `debug`/`debugFile`, `promptSuggestions`
|
|
739
833
|
- `ToolPermissionContext` missing `blockedPath`, `decisionReason`, `toolUseID`, `agentID`, `description`
|
|
834
|
+
- Has `agent_type` field in `SubagentStopHookInput`
|
|
740
835
|
- Has SDK MCP server support with `tool()` helper and annotations
|
|
741
836
|
- Added `thinking` config and `effort` option in v0.1.36
|
|
742
837
|
- Handles `rate_limit_event` and unknown message types gracefully (v0.1.40)
|
|
743
838
|
- Client has `get_server_info()` for accessing the initialization result (v0.1.31+)
|
|
744
|
-
- v0.1.42 – v0.1.
|
|
839
|
+
- v0.1.42 – v0.1.44: CLI parity updates (no new SDK-facing features; latest commit bumps bundled CLI to v2.1.61)
|
|
745
840
|
|
|
746
841
|
### Ruby SDK (This Repository)
|
|
747
|
-
- Feature parity with TypeScript SDK v0.2.
|
|
842
|
+
- Feature parity with TypeScript SDK v0.2.62
|
|
748
843
|
- Ruby-idiomatic patterns (Data.define, snake_case)
|
|
749
844
|
- Complete control protocol, hook, and V2 Session API support
|
|
750
845
|
- Dedicated Client class for multi-turn conversations
|
|
751
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
|
|