claude-agent-sdk 0.16.5 → 0.16.7
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 +19 -0
- data/README.md +102 -29
- data/lib/claude_agent_sdk/command_builder.rb +14 -0
- data/lib/claude_agent_sdk/message_parser.rb +29 -186
- data/lib/claude_agent_sdk/types.rb +574 -954
- data/lib/claude_agent_sdk/version.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b2b002f964cd13c118fd417aa0ed253fe47f469d9c7ad75f3839f64177e7744
|
|
4
|
+
data.tar.gz: a253123d762a01fe8cbfc6f7a798d9ed310c56f8e8970393d2dd053d44c33b27
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc338d1a24133685314b1696fc698b709f16b79dd620bb002d5ba9106aea29ce8575894e0120e3885ebaeccc78432c42942d5b642d1695e77264008fb1eb9b6e
|
|
7
|
+
data.tar.gz: 4630953488a3ec83df9be0209cb2731ffd80efdb5b37b6051df11cda5735928a83c98a11db9f55c869e2108ec015e856b33f0f75a2dac2873f022fbbd68d3db2
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.16.7] - 2026-05-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `ClaudeAgentOptions#resume_session_at` — forwarded as `--resume-session-at <message-uuid>` to the CLI. When resuming a session, the conversation is truncated to include only messages up to and including the assistant message with the given UUID, enabling history rewriting / branched continuations from a specific turn. Raises `ArgumentError` from `CommandBuilder` if set without `resume` (matches the CLI's own validation but surfaces it synchronously in the caller's stack).
|
|
14
|
+
- `examples/e2b_transport_example.rb` — working ~320-line custom transport that runs the Claude Code CLI inside an E2B Firecracker microVM via the `e2b` gem. Reuses `CommandBuilder` for argv parity with `SubprocessCLITransport` and demonstrates the 6-method `Transport` interface against a remote backend. README's Custom Transport section now includes an interface table, data-flow diagram, code sketch, and production-hardening checklist that link to the example.
|
|
15
|
+
|
|
16
|
+
## [0.16.6] - 2026-04-29
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Type classes now accept hash construction with mixed key shapes — symbols or strings, snake_case or camelCase — and support bracket access (`obj[:field]`, `obj['field']`). `UserMessage.new({"sessionId" => "abc"})` works the same as `UserMessage.new(session_id: "abc")`. `Type.from_hash(nil)` and `Type.wrap(nil)` are nil-safe.
|
|
20
|
+
- `ClaudeAgentOptions.new(nil)` is accepted and yields an options object populated with configured defaults.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- Discriminator fields on type classes (`type` on `SystemPromptFile`/`McpStdioServerConfig`/etc., `behavior` on `PermissionResultAllow`/`Deny`, `hook_event_name` on every hook input/output) are now `attr_reader`-only, set authoritatively in `initialize`. They cannot be overwritten externally.
|
|
24
|
+
- `ClaudeAgentOptions` continues to raise `ArgumentError` on unknown keys (constructor, `dup_with`, and `[]=`); other type subclasses silently drop unknown keys for forward-compatibility with newer CLI output.
|
|
25
|
+
|
|
26
|
+
### Internal
|
|
27
|
+
- Unified ~50 type classes (messages, hook inputs/outputs, MCP configs, system prompt configs, sandbox settings) under a shared `Type` base class. `lib/claude_agent_sdk/types.rb` shrank from 1510 to 588 lines; `MessageParser`'s system-message dispatch went from 215 lines to a 14-entry lookup table.
|
|
28
|
+
|
|
10
29
|
## [0.16.5] - 2026-04-24
|
|
11
30
|
|
|
12
31
|
### Added
|
data/README.md
CHANGED
|
@@ -108,7 +108,7 @@ Add this line to your application's Gemfile:
|
|
|
108
108
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
109
109
|
|
|
110
110
|
# Or use a stable version from RubyGems
|
|
111
|
-
gem 'claude-agent-sdk', '~> 0.16.
|
|
111
|
+
gem 'claude-agent-sdk', '~> 0.16.7'
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
And then execute:
|
|
@@ -311,57 +311,112 @@ end.wait
|
|
|
311
311
|
|
|
312
312
|
### Custom Transport
|
|
313
313
|
|
|
314
|
-
By default, `Client` uses `SubprocessCLITransport` to spawn the Claude Code CLI locally. You can provide a custom transport class to connect via other channels (e.g.,
|
|
314
|
+
By default, `Client` uses `SubprocessCLITransport` to spawn the Claude Code CLI locally. You can provide a custom transport class to connect via other channels (e.g., remote SSH, WebSocket, or a sandbox VM).
|
|
315
|
+
|
|
316
|
+
A transport must implement six methods:
|
|
317
|
+
|
|
318
|
+
| Method | Purpose |
|
|
319
|
+
|---|---|
|
|
320
|
+
| `connect` | Establish the connection / spawn the remote CLI |
|
|
321
|
+
| `write(data)` | Send raw JSON-line bytes to stdin |
|
|
322
|
+
| `read_messages { \|hash\| ... }` | Yield parsed JSON messages from stdout; block until the stream closes |
|
|
323
|
+
| `end_input` | Signal EOF on stdin |
|
|
324
|
+
| `close` | Terminate and clean up |
|
|
325
|
+
| `ready?` | Report whether the transport can accept I/O |
|
|
326
|
+
|
|
327
|
+
Then plug it into `Client` via `transport_class:` / `transport_args:`. All connect orchestration (option transforms, MCP extraction, hook conversion, Query lifecycle) is handled for you.
|
|
315
328
|
|
|
316
329
|
```ruby
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
end
|
|
330
|
+
client = ClaudeAgentSDK::Client.new(
|
|
331
|
+
options: options,
|
|
332
|
+
transport_class: MyTransport,
|
|
333
|
+
transport_args: { foo: 'bar' } # forwarded to MyTransport.new(options, **transport_args)
|
|
334
|
+
)
|
|
335
|
+
```
|
|
324
336
|
|
|
325
|
-
|
|
326
|
-
@sandbox.connect
|
|
327
|
-
end
|
|
337
|
+
#### Reference: running `claude` inside an E2B sandbox
|
|
328
338
|
|
|
329
|
-
|
|
330
|
-
@sandbox.stdin_write(data)
|
|
331
|
-
end
|
|
339
|
+
[`examples/e2b_transport_example.rb`](examples/e2b_transport_example.rb) is a working transport that runs the Claude Code CLI inside an [E2B](https://e2b.dev) Firecracker microVM instead of on your host. The wire protocol stays identical — only the I/O layer changes:
|
|
332
340
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
341
|
+
```
|
|
342
|
+
ClaudeAgentSDK::Client (host)
|
|
343
|
+
│ JSON-lines
|
|
344
|
+
▼
|
|
345
|
+
E2BCliTransport (host)
|
|
346
|
+
│ send_stdin / commands.run(background:) / CommandHandle#each
|
|
347
|
+
▼
|
|
348
|
+
E2B envd RPC (HTTP/2)
|
|
349
|
+
│
|
|
350
|
+
▼
|
|
351
|
+
/usr/local/bin/claude (in-VM subprocess)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The example reuses the SDK's `CommandBuilder` to produce the exact same argv that `SubprocessCLITransport` would build (including SDK MCP server `:instance` field stripping), shell-escapes it for E2B's `/bin/bash -l -c` execution path, and streams stdout/stderr back through `CommandHandle#each`.
|
|
355
|
+
|
|
356
|
+
Sketch (full file is ~250 lines):
|
|
336
357
|
|
|
337
|
-
|
|
338
|
-
|
|
358
|
+
```ruby
|
|
359
|
+
require 'claude_agent_sdk'
|
|
360
|
+
require 'e2b'
|
|
361
|
+
|
|
362
|
+
class E2BCliTransport < ClaudeAgentSDK::Transport
|
|
363
|
+
def initialize(options, sandbox:, cli_path: '/usr/local/bin/claude')
|
|
364
|
+
@options, @sandbox, @cli_path = options, sandbox, cli_path
|
|
339
365
|
end
|
|
340
366
|
|
|
341
|
-
def
|
|
342
|
-
@
|
|
367
|
+
def connect
|
|
368
|
+
argv = ClaudeAgentSDK::CommandBuilder.new(@cli_path, @options).build
|
|
369
|
+
cmd = argv.map { |a| Shellwords.shellescape(a.to_s) }.join(' ')
|
|
370
|
+
@handle = @sandbox.commands.run(cmd, background: true, stdin: true,
|
|
371
|
+
cwd: @options.cwd&.to_s, envs: build_env)
|
|
372
|
+
@pid = @handle.pid
|
|
373
|
+
@ready = true
|
|
343
374
|
end
|
|
344
375
|
|
|
345
|
-
def
|
|
346
|
-
|
|
376
|
+
def write(data) = @sandbox.commands.send_stdin(@pid, data)
|
|
377
|
+
def end_input = @sandbox.commands.close_stdin(@pid)
|
|
378
|
+
def close = @handle&.kill
|
|
379
|
+
def ready? = @ready
|
|
380
|
+
|
|
381
|
+
def read_messages(&block)
|
|
382
|
+
buf = +''
|
|
383
|
+
@handle.each do |stdout, stderr, _pty|
|
|
384
|
+
next if stderr && !stderr.empty?
|
|
385
|
+
stdout.each_line do |line|
|
|
386
|
+
buf << line.strip
|
|
387
|
+
begin
|
|
388
|
+
yield JSON.parse(buf, symbolize_names: true)
|
|
389
|
+
buf.clear
|
|
390
|
+
rescue JSON::ParserError
|
|
391
|
+
# JSON line split across reads — keep buffering
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
@handle.wait # raises E2B::CommandExitError on non-zero exit
|
|
347
396
|
end
|
|
348
397
|
end
|
|
349
398
|
|
|
350
|
-
# Use it
|
|
351
|
-
|
|
399
|
+
# Use it
|
|
400
|
+
sandbox = E2B::Sandbox.create(template: 'base', timeout: 600)
|
|
352
401
|
Async do
|
|
353
402
|
client = ClaudeAgentSDK::Client.new(
|
|
354
403
|
options: options,
|
|
355
|
-
transport_class:
|
|
356
|
-
transport_args: { sandbox:
|
|
404
|
+
transport_class: E2BCliTransport,
|
|
405
|
+
transport_args: { sandbox: sandbox }
|
|
357
406
|
)
|
|
358
407
|
client.connect
|
|
359
|
-
client.query(
|
|
408
|
+
client.query('Hello from the sandbox!')
|
|
360
409
|
client.receive_response { |msg| puts msg }
|
|
361
410
|
client.disconnect
|
|
411
|
+
ensure
|
|
412
|
+
sandbox.kill
|
|
362
413
|
end.wait
|
|
363
414
|
```
|
|
364
415
|
|
|
416
|
+
**Why use a remote transport?** Untrusted code execution, multi-tenant agent runs that can't share a host, environments without local Node.js, or simply isolating filesystem/network blast radius. The Firecracker VM gives you a fresh `/home/user` per session and is killable without touching the host.
|
|
417
|
+
|
|
418
|
+
**Production hardening** (intentionally omitted from the example for clarity): inactivity watchdog, keepalive heartbeat, stream reconnect on transient SSL/EOF errors, host env-var blocklist, MCP server filtering for sandbox compatibility. See the example file's header comments for what to add and why.
|
|
419
|
+
|
|
365
420
|
## Custom Tools (SDK MCP Servers)
|
|
366
421
|
|
|
367
422
|
A **custom tool** is a Ruby proc/lambda that you can offer to Claude, for Claude to invoke as needed.
|
|
@@ -1053,6 +1108,24 @@ result = ClaudeAgentSDK.fork_session(
|
|
|
1053
1108
|
|
|
1054
1109
|
> **Note:** Session mutations use append-only JSONL writes with `O_WRONLY | O_APPEND` (no `O_CREAT`) for TOCTOU safety. They are safe to call while the session is open in a CLI process. `fork_session` uses `O_CREAT | O_EXCL` to prevent race conditions.
|
|
1055
1110
|
|
|
1111
|
+
### Resuming at a Specific Message
|
|
1112
|
+
|
|
1113
|
+
`resume_session_at` truncates the resumed conversation to messages up to **and including** the assistant message with the given UUID — useful for rewriting history from a known point or branching exploration without forking the session file. The flag rides on top of `resume`, so the original session ID is preserved; only the in-memory history loaded for the new turn is shortened.
|
|
1114
|
+
|
|
1115
|
+
```ruby
|
|
1116
|
+
ClaudeAgentSDK.query(
|
|
1117
|
+
prompt: 'Try a different approach',
|
|
1118
|
+
options: ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
1119
|
+
resume: '550e8400-e29b-41d4-a716-446655440000',
|
|
1120
|
+
resume_session_at: 'assistant-message-uuid-from-history'
|
|
1121
|
+
)
|
|
1122
|
+
) do |message|
|
|
1123
|
+
# ...
|
|
1124
|
+
end
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
`resume_session_at` requires `resume`; the SDK raises `ArgumentError` from `CommandBuilder` when this constraint is violated, matching the underlying CLI's validation but surfacing it synchronously in the caller's stack.
|
|
1128
|
+
|
|
1056
1129
|
## Observability (OpenTelemetry / Langfuse)
|
|
1057
1130
|
|
|
1058
1131
|
The SDK includes a built-in **observer interface** and an **OpenTelemetry observer** for tracing agent sessions. Traces are emitted using standard `gen_ai.*` semantic conventions, compatible with Langfuse, Jaeger, Datadog, and any OTel backend.
|
|
@@ -105,9 +105,23 @@ module ClaudeAgentSDK
|
|
|
105
105
|
def append_session(cmd)
|
|
106
106
|
cmd.push("--continue") if @options.continue_conversation
|
|
107
107
|
cmd.push("--resume", @options.resume) if @options.resume
|
|
108
|
+
append_resume_session_at(cmd)
|
|
108
109
|
cmd.push("--session-id", @options.session_id) if @options.session_id
|
|
109
110
|
end
|
|
110
111
|
|
|
112
|
+
# `--resume-session-at <message-uuid>` truncates the resumed conversation
|
|
113
|
+
# to include messages up to and including the given assistant message UUID.
|
|
114
|
+
# The CLI rejects this flag without `--resume`; we raise early to surface
|
|
115
|
+
# the misconfiguration in the caller's stack rather than as a remote
|
|
116
|
+
# process exit.
|
|
117
|
+
def append_resume_session_at(cmd)
|
|
118
|
+
return unless @options.resume_session_at
|
|
119
|
+
|
|
120
|
+
raise ArgumentError, "resume_session_at requires resume to be set" unless @options.resume
|
|
121
|
+
|
|
122
|
+
cmd.push("--resume-session-at", @options.resume_session_at.to_s)
|
|
123
|
+
end
|
|
124
|
+
|
|
111
125
|
def append_settings(cmd)
|
|
112
126
|
return unless @options.settings || @options.sandbox
|
|
113
127
|
|
|
@@ -78,214 +78,57 @@ module ClaudeAgentSDK
|
|
|
78
78
|
)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
|
+
# Typed SystemMessage subclasses inherit from `Type` and accept the raw
|
|
82
|
+
# CLI hash directly — camelCase and snake_case keys are normalized by the
|
|
83
|
+
# base class, and the full hash is captured as `#data`.
|
|
84
|
+
SYSTEM_MESSAGE_CLASSES = {
|
|
85
|
+
'init' => InitMessage,
|
|
86
|
+
'compact_boundary' => CompactBoundaryMessage,
|
|
87
|
+
'status' => StatusMessage,
|
|
88
|
+
'api_retry' => APIRetryMessage,
|
|
89
|
+
'local_command_output' => LocalCommandOutputMessage,
|
|
90
|
+
'hook_started' => HookStartedMessage,
|
|
91
|
+
'hook_progress' => HookProgressMessage,
|
|
92
|
+
'hook_response' => HookResponseMessage,
|
|
93
|
+
'session_state_changed' => SessionStateChangedMessage,
|
|
94
|
+
'files_persisted' => FilesPersistedMessage,
|
|
95
|
+
'elicitation_complete' => ElicitationCompleteMessage,
|
|
96
|
+
'task_started' => TaskStartedMessage,
|
|
97
|
+
'task_progress' => TaskProgressMessage,
|
|
98
|
+
'task_notification' => TaskNotificationMessage
|
|
99
|
+
}.freeze
|
|
100
|
+
|
|
81
101
|
def self.parse_system_message(data)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
InitMessage.new(
|
|
85
|
-
subtype: data[:subtype], data: data,
|
|
86
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
87
|
-
agents: data[:agents], api_key_source: data[:apiKeySource] || data[:api_key_source],
|
|
88
|
-
betas: data[:betas], claude_code_version: data[:claude_code_version],
|
|
89
|
-
cwd: data[:cwd], tools: data[:tools], mcp_servers: data[:mcp_servers],
|
|
90
|
-
model: data[:model], permission_mode: data[:permissionMode] || data[:permission_mode],
|
|
91
|
-
slash_commands: data[:slash_commands], output_style: data[:output_style],
|
|
92
|
-
skills: data[:skills], plugins: data[:plugins],
|
|
93
|
-
fast_mode_state: data[:fastModeState] || data[:fast_mode_state]
|
|
94
|
-
)
|
|
95
|
-
when 'compact_boundary'
|
|
96
|
-
raw_metadata = data[:compact_metadata]
|
|
97
|
-
CompactBoundaryMessage.new(
|
|
98
|
-
subtype: data[:subtype], data: data,
|
|
99
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
100
|
-
compact_metadata: CompactMetadata.from_hash(raw_metadata)
|
|
101
|
-
)
|
|
102
|
-
when 'status'
|
|
103
|
-
StatusMessage.new(
|
|
104
|
-
subtype: data[:subtype], data: data,
|
|
105
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
106
|
-
status: data[:status],
|
|
107
|
-
permission_mode: data[:permissionMode] || data[:permission_mode]
|
|
108
|
-
)
|
|
109
|
-
when 'api_retry'
|
|
110
|
-
APIRetryMessage.new(
|
|
111
|
-
subtype: data[:subtype], data: data,
|
|
112
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
113
|
-
attempt: data[:attempt], max_retries: data[:maxRetries] || data[:max_retries],
|
|
114
|
-
retry_delay_ms: data[:retryDelayMs] || data[:retry_delay_ms],
|
|
115
|
-
error_status: data[:errorStatus] || data[:error_status],
|
|
116
|
-
error: data[:error]
|
|
117
|
-
)
|
|
118
|
-
when 'local_command_output'
|
|
119
|
-
LocalCommandOutputMessage.new(
|
|
120
|
-
subtype: data[:subtype], data: data,
|
|
121
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
122
|
-
content: data[:content]
|
|
123
|
-
)
|
|
124
|
-
when 'hook_started'
|
|
125
|
-
HookStartedMessage.new(
|
|
126
|
-
subtype: data[:subtype], data: data,
|
|
127
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
128
|
-
hook_id: data[:hookId] || data[:hook_id],
|
|
129
|
-
hook_name: data[:hookName] || data[:hook_name],
|
|
130
|
-
hook_event: data[:hookEvent] || data[:hook_event]
|
|
131
|
-
)
|
|
132
|
-
when 'hook_progress'
|
|
133
|
-
HookProgressMessage.new(
|
|
134
|
-
subtype: data[:subtype], data: data,
|
|
135
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
136
|
-
hook_id: data[:hookId] || data[:hook_id],
|
|
137
|
-
hook_name: data[:hookName] || data[:hook_name],
|
|
138
|
-
hook_event: data[:hookEvent] || data[:hook_event],
|
|
139
|
-
stdout: data[:stdout], stderr: data[:stderr], output: data[:output]
|
|
140
|
-
)
|
|
141
|
-
when 'hook_response'
|
|
142
|
-
HookResponseMessage.new(
|
|
143
|
-
subtype: data[:subtype], data: data,
|
|
144
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
145
|
-
hook_id: data[:hookId] || data[:hook_id],
|
|
146
|
-
hook_name: data[:hookName] || data[:hook_name],
|
|
147
|
-
hook_event: data[:hookEvent] || data[:hook_event],
|
|
148
|
-
output: data[:output], stdout: data[:stdout], stderr: data[:stderr],
|
|
149
|
-
exit_code: data[:exitCode] || data[:exit_code],
|
|
150
|
-
outcome: data[:outcome]
|
|
151
|
-
)
|
|
152
|
-
when 'session_state_changed'
|
|
153
|
-
SessionStateChangedMessage.new(
|
|
154
|
-
subtype: data[:subtype], data: data,
|
|
155
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
156
|
-
state: data[:state]
|
|
157
|
-
)
|
|
158
|
-
when 'files_persisted'
|
|
159
|
-
FilesPersistedMessage.new(
|
|
160
|
-
subtype: data[:subtype], data: data,
|
|
161
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
162
|
-
files: data[:files], failed: data[:failed],
|
|
163
|
-
processed_at: data[:processedAt] || data[:processed_at]
|
|
164
|
-
)
|
|
165
|
-
when 'elicitation_complete'
|
|
166
|
-
ElicitationCompleteMessage.new(
|
|
167
|
-
subtype: data[:subtype], data: data,
|
|
168
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
169
|
-
mcp_server_name: data[:mcpServerName] || data[:mcp_server_name],
|
|
170
|
-
elicitation_id: data[:elicitationId] || data[:elicitation_id]
|
|
171
|
-
)
|
|
172
|
-
when 'task_started'
|
|
173
|
-
TaskStartedMessage.new(
|
|
174
|
-
subtype: data[:subtype], data: data,
|
|
175
|
-
task_id: data[:task_id], description: data[:description],
|
|
176
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
177
|
-
tool_use_id: data[:tool_use_id], task_type: data[:task_type],
|
|
178
|
-
workflow_name: data[:workflowName] || data[:workflow_name],
|
|
179
|
-
prompt: data[:prompt]
|
|
180
|
-
)
|
|
181
|
-
when 'task_progress'
|
|
182
|
-
TaskProgressMessage.new(
|
|
183
|
-
subtype: data[:subtype], data: data,
|
|
184
|
-
task_id: data[:task_id], description: data[:description],
|
|
185
|
-
usage: data[:usage], uuid: data[:uuid], session_id: data[:session_id],
|
|
186
|
-
tool_use_id: data[:tool_use_id], last_tool_name: data[:last_tool_name],
|
|
187
|
-
summary: data[:summary]
|
|
188
|
-
)
|
|
189
|
-
when 'task_notification'
|
|
190
|
-
TaskNotificationMessage.new(
|
|
191
|
-
subtype: data[:subtype], data: data,
|
|
192
|
-
task_id: data[:task_id], status: data[:status],
|
|
193
|
-
output_file: data[:output_file], summary: data[:summary],
|
|
194
|
-
uuid: data[:uuid], session_id: data[:session_id],
|
|
195
|
-
tool_use_id: data[:tool_use_id], usage: data[:usage]
|
|
196
|
-
)
|
|
197
|
-
else
|
|
198
|
-
SystemMessage.new(subtype: data[:subtype], data: data)
|
|
199
|
-
end
|
|
102
|
+
klass = SYSTEM_MESSAGE_CLASSES[data[:subtype]] || SystemMessage
|
|
103
|
+
klass.new(data)
|
|
200
104
|
end
|
|
201
105
|
|
|
202
106
|
def self.parse_result_message(data)
|
|
203
|
-
ResultMessage.new(
|
|
204
|
-
subtype: data[:subtype],
|
|
205
|
-
duration_ms: data[:duration_ms],
|
|
206
|
-
duration_api_ms: data[:duration_api_ms],
|
|
207
|
-
is_error: data[:is_error],
|
|
208
|
-
num_turns: data[:num_turns],
|
|
209
|
-
session_id: data[:session_id],
|
|
210
|
-
stop_reason: data[:stop_reason],
|
|
211
|
-
total_cost_usd: data[:total_cost_usd],
|
|
212
|
-
usage: data[:usage],
|
|
213
|
-
result: data[:result],
|
|
214
|
-
structured_output: data[:structured_output],
|
|
215
|
-
model_usage: data[:modelUsage] || data[:model_usage],
|
|
216
|
-
permission_denials: data[:permission_denials],
|
|
217
|
-
errors: data[:errors],
|
|
218
|
-
uuid: data[:uuid],
|
|
219
|
-
fast_mode_state: data[:fastModeState] || data[:fast_mode_state]
|
|
220
|
-
)
|
|
107
|
+
ResultMessage.new(data)
|
|
221
108
|
end
|
|
222
109
|
|
|
223
110
|
def self.parse_stream_event(data)
|
|
224
|
-
StreamEvent.new(
|
|
225
|
-
uuid: data[:uuid],
|
|
226
|
-
session_id: data[:session_id],
|
|
227
|
-
event: data[:event],
|
|
228
|
-
parent_tool_use_id: data[:parent_tool_use_id]
|
|
229
|
-
)
|
|
111
|
+
StreamEvent.new(data)
|
|
230
112
|
end
|
|
231
113
|
|
|
232
114
|
def self.parse_rate_limit_event(data)
|
|
233
|
-
|
|
234
|
-
rate_limit_info = RateLimitInfo.new(
|
|
235
|
-
status: info[:status],
|
|
236
|
-
resets_at: info[:resetsAt],
|
|
237
|
-
rate_limit_type: info[:rateLimitType],
|
|
238
|
-
utilization: info[:utilization],
|
|
239
|
-
overage_status: info[:overageStatus],
|
|
240
|
-
overage_resets_at: info[:overageResetsAt],
|
|
241
|
-
overage_disabled_reason: info[:overageDisabledReason],
|
|
242
|
-
raw: info
|
|
243
|
-
)
|
|
244
|
-
RateLimitEvent.new(
|
|
245
|
-
rate_limit_info: rate_limit_info,
|
|
246
|
-
uuid: data[:uuid],
|
|
247
|
-
session_id: data[:session_id],
|
|
248
|
-
raw_data: data
|
|
249
|
-
)
|
|
115
|
+
RateLimitEvent.new(data.merge(raw_data: data))
|
|
250
116
|
end
|
|
251
117
|
|
|
252
118
|
def self.parse_tool_progress_message(data)
|
|
253
|
-
ToolProgressMessage.new(
|
|
254
|
-
uuid: data[:uuid],
|
|
255
|
-
session_id: data[:session_id],
|
|
256
|
-
tool_use_id: data[:toolUseId] || data[:tool_use_id],
|
|
257
|
-
tool_name: data[:toolName] || data[:tool_name],
|
|
258
|
-
parent_tool_use_id: data[:parentToolUseId] || data[:parent_tool_use_id],
|
|
259
|
-
elapsed_time_seconds: data[:elapsedTimeSeconds] || data[:elapsed_time_seconds],
|
|
260
|
-
task_id: data[:taskId] || data[:task_id]
|
|
261
|
-
)
|
|
119
|
+
ToolProgressMessage.new(data)
|
|
262
120
|
end
|
|
263
121
|
|
|
264
122
|
def self.parse_auth_status_message(data)
|
|
265
|
-
AuthStatusMessage.new(
|
|
266
|
-
uuid: data[:uuid],
|
|
267
|
-
session_id: data[:session_id],
|
|
268
|
-
is_authenticating: data[:isAuthenticating] || data[:is_authenticating],
|
|
269
|
-
output: data[:output],
|
|
270
|
-
error: data[:error]
|
|
271
|
-
)
|
|
123
|
+
AuthStatusMessage.new(data)
|
|
272
124
|
end
|
|
273
125
|
|
|
274
126
|
def self.parse_tool_use_summary_message(data)
|
|
275
|
-
ToolUseSummaryMessage.new(
|
|
276
|
-
uuid: data[:uuid],
|
|
277
|
-
session_id: data[:session_id],
|
|
278
|
-
summary: data[:summary],
|
|
279
|
-
preceding_tool_use_ids: data[:precedingToolUseIds] || data[:preceding_tool_use_ids]
|
|
280
|
-
)
|
|
127
|
+
ToolUseSummaryMessage.new(data)
|
|
281
128
|
end
|
|
282
129
|
|
|
283
130
|
def self.parse_prompt_suggestion_message(data)
|
|
284
|
-
PromptSuggestionMessage.new(
|
|
285
|
-
uuid: data[:uuid],
|
|
286
|
-
session_id: data[:session_id],
|
|
287
|
-
suggestion: data[:suggestion]
|
|
288
|
-
)
|
|
131
|
+
PromptSuggestionMessage.new(data)
|
|
289
132
|
end
|
|
290
133
|
|
|
291
134
|
# Accepts blocks with either symbol or string keys — live CLI messages
|