claude-agent-sdk 1.2.1 → 1.4.0
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 +94 -71
- data/README.md +93 -71
- data/UPGRADING-1.0.md +1 -1
- data/docs/client.md +3 -1
- data/docs/configuration.md +6 -6
- data/docs/errors.md +28 -2
- data/docs/hooks-and-permissions.md +36 -7
- data/docs/mcp-servers.md +1 -1
- data/docs/observability.md +1 -1
- data/docs/options.md +43 -0
- data/docs/rails.md +5 -5
- data/docs/sessions.md +1 -1
- data/docs/subagents.md +1 -1
- data/docs/types.md +2 -2
- data/lib/claude_agent_sdk/cli_installer.rb +1 -1
- data/lib/claude_agent_sdk/query.rb +48 -220
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +9 -0
- data/lib/claude_agent_sdk/types/hooks.rb +378 -243
- data/lib/claude_agent_sdk/types/messages.rb +14 -3
- data/lib/claude_agent_sdk/types/options.rb +35 -0
- data/lib/claude_agent_sdk/version.rb +1 -1
- data/lib/generators/claude_agent_sdk/install/install_generator.rb +1 -1
- data/lib/generators/claude_agent_sdk/install/templates/claude_agent_sdk.rb.tt +1 -1
- data/sig/claude_agent_sdk/types/hooks.rbs +286 -3
- data/sig/claude_agent_sdk/types/messages.rbs +15 -0
- data/sig/claude_agent_sdk/types/options.rbs +9 -0
- metadata +6 -6
data/README.md
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# Claude Agent SDK for Ruby
|
|
4
4
|
|
|
5
5
|
[](https://rubygems.org/gems/claude-agent-sdk)
|
|
6
|
-
[](https://github.com/rubycatco/claude-agent-sdk-ruby/actions/workflows/ci.yml)
|
|
7
7
|
[](https://www.ruby-lang.org/)
|
|
8
8
|
[](https://rubydoc.info/gems/claude-agent-sdk)
|
|
9
|
-
[](https://github.com/
|
|
9
|
+
[](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/LICENSE)
|
|
10
10
|
|
|
11
|
-
A Ruby SDK for the [Claude Code](https://
|
|
11
|
+
A Ruby SDK for the [Claude Code](https://code.claude.com/docs/en/overview) agent runtime, built for running agents in production Ruby and Rails apps. It has the same capabilities as the official [TypeScript](https://github.com/anthropics/claude-agent-sdk-typescript) and [Python](https://github.com/anthropics/claude-agent-sdk-python) SDKs, plus what a Rails deploy needs around them: a generator and CLI-vendoring rake task, callbacks that are safe to touch ActiveRecord from, a pinned CLI binary, built-in OpenTelemetry tracing, and transcript mirroring to your own storage.
|
|
12
12
|
|
|
13
|
-
> **Unofficial and community-maintained.** This project is not affiliated with or supported by Anthropic. It tracks the official SDKs release by release; see the [CHANGELOG](https://github.com/
|
|
14
|
-
|
|
15
|
-
> **Upgrading from 0.x?** 1.0 raises on unknown keys, limits `#[]` to attributes and adds `SessionStoreError`. [UPGRADING-1.0.md](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/UPGRADING-1.0.md) has the checklist.
|
|
13
|
+
> **Unofficial and community-maintained.** This project is not affiliated with or supported by Anthropic. It tracks the official SDKs release by release; see the [CHANGELOG](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/CHANGELOG.md) for the currently synced version.
|
|
16
14
|
|
|
17
15
|
## Highlights
|
|
18
16
|
|
|
19
|
-
- **Rails integration.** `bin/rails generate claude_agent_sdk:install` writes the initializer and `bin/rails claude_agent_sdk:install_cli` vendors the CLI; [docs/rails.md](https://github.com/
|
|
17
|
+
- **Rails integration.** `bin/rails generate claude_agent_sdk:install` writes the initializer and `bin/rails claude_agent_sdk:install_cli` vendors the CLI; [docs/rails.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/rails.md) covers jobs, ActionCable streaming, session resumption, and solid_queue fiber workers (`callback_scheduling: :inline`).
|
|
20
18
|
- **Callbacks that are safe around ActiveRecord.** Tool handlers, hooks, permission callbacks, and message blocks run on a plain thread by default, outside the SDK's fiber scheduler, so thread-keyed libraries (ActiveRecord, `pg`, per-thread caches) behave as they do everywhere else in your app. `ClaudeAgentSDK::Railtie.callback_wrapper` runs them in the Rails executor so connections go back to the pool, without deadlocking development code reloading.
|
|
21
19
|
- **Hermetic deploys.** `CLIInstaller` vendors a checksum-verified CLI binary, pinned to the version each gem release is tested with, so production never depends on a global `npm install`.
|
|
22
20
|
- **Built-in OpenTelemetry observer** with Langfuse support; no third-party instrumentation library required.
|
|
@@ -24,7 +22,7 @@ A Ruby SDK for the [Claude Code](https://docs.claude.com/en/docs/claude-code-ove
|
|
|
24
22
|
- **Same wire protocol as the official SDKs.** Spawns the `claude` CLI as a subprocess and speaks stream-JSON over stdin/stdout, so every feature of the runtime is available: sessions, subagents, sandboxing, structured output, file checkpointing and rewind.
|
|
25
23
|
- **`query()` for one-shot calls, `Client` for bidirectional sessions** with interrupts, mid-session model switching, and streaming input from any `Enumerator`.
|
|
26
24
|
- **In-process custom tools.** Define tools as Ruby blocks; they run inside your process with direct access to your app state (SDK MCP servers), with JSON-Schema-validated arguments.
|
|
27
|
-
- **All
|
|
25
|
+
- **All 33 hook events and permission callbacks** with typed inputs, so you can gate, audit, or rewrite every tool call.
|
|
28
26
|
- **Pluggable transport** to run the CLI somewhere else (an E2B microVM, a container, over SSH).
|
|
29
27
|
|
|
30
28
|
## Installation
|
|
@@ -34,7 +32,9 @@ A Ruby SDK for the [Claude Code](https://docs.claude.com/en/docs/claude-code-ove
|
|
|
34
32
|
gem 'claude-agent-sdk', '~> 1.0'
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
Then `bundle install`, or install directly with `gem install claude-agent-sdk`. To track unreleased changes, point the Gemfile at GitHub: `gem 'claude-agent-sdk', github: '
|
|
35
|
+
Then `bundle install`, or install directly with `gem install claude-agent-sdk`. To track unreleased changes, point the Gemfile at GitHub: `gem 'claude-agent-sdk', github: 'rubycatco/claude-agent-sdk-ruby'`.
|
|
36
|
+
|
|
37
|
+
> **Upgrading from 0.x?** 1.0 raises on unknown keys, limits `#[]` to attributes and adds `SessionStoreError`. [UPGRADING-1.0.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/UPGRADING-1.0.md) has the checklist.
|
|
38
38
|
|
|
39
39
|
**Prerequisites**
|
|
40
40
|
|
|
@@ -47,7 +47,7 @@ Then `bundle install`, or install directly with `gem install claude-agent-sdk`.
|
|
|
47
47
|
ClaudeAgentSDK::CLIInstaller.install_pinned # => "/app/vendor/claude/claude"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
`install_pinned` installs `CLIInstaller::PINNED_CLI_VERSION`, so upgrading the gem carries the CLI forward with it; `CLIInstaller.install(version: 'x.y.z')` pins a version of your own. The vendored binary is found ahead of `PATH`, installs are idempotent and concurrency-safe, and a failed upgrade never breaks a working install. See [docs/cli-installer.md](https://github.com/
|
|
50
|
+
`install_pinned` installs `CLIInstaller::PINNED_CLI_VERSION`, so upgrading the gem carries the CLI forward with it; `CLIInstaller.install(version: 'x.y.z')` pins a version of your own. The vendored binary is found ahead of `PATH`, installs are idempotent and concurrency-safe, and a failed upgrade never breaks a working install. See [docs/cli-installer.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/cli-installer.md) for the full behaviour, supported platforms, and the CLI discovery order.
|
|
51
51
|
|
|
52
52
|
### Authentication
|
|
53
53
|
|
|
@@ -57,31 +57,7 @@ The SDK holds no credentials of its own. The `claude` process it starts authenti
|
|
|
57
57
|
- `CLAUDE_CODE_OAUTH_TOKEN`, a long-lived token for a Claude subscription (`claude setup-token` creates one), set the same way
|
|
58
58
|
- a login the CLI has already stored for the user your process runs as (`claude auth login`)
|
|
59
59
|
|
|
60
|
-
A machine with none of them, such as a fresh container or a CI runner, does not fail at startup
|
|
61
|
-
|
|
62
|
-
### Rails in a minute
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
bundle add claude-agent-sdk
|
|
66
|
-
bin/rails generate claude_agent_sdk:install # config/initializers/claude_agent_sdk.rb + .gitignore entry
|
|
67
|
-
bin/rails claude_agent_sdk:install_cli # the tested CLI into vendor/claude (also a Docker build step)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
```ruby
|
|
71
|
-
# app/jobs/summarize_ticket_job.rb
|
|
72
|
-
class SummarizeTicketJob < ApplicationJob
|
|
73
|
-
def perform(ticket)
|
|
74
|
-
options = ClaudeAgentSDK::ClaudeAgentOptions.new(tools: [], max_turns: 1)
|
|
75
|
-
prompt = "Summarize this support ticket in two sentences:\n\n#{ticket.body}"
|
|
76
|
-
|
|
77
|
-
ClaudeAgentSDK.query(prompt: prompt, options: options) do |message|
|
|
78
|
-
ticket.update!(summary: message.result) if message.is_a?(ClaudeAgentSDK::ResultMessage)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
The block runs on a plain thread, so ActiveRecord calls inside it just work. [docs/rails.md](https://github.com/ya-luotao/claude-agent-sdk-ruby/blob/main/docs/rails.md) continues with multi-turn sessions, ActionCable streaming, and fiber workers.
|
|
60
|
+
A machine with none of them, such as a fresh container or a CI runner, does not fail at startup: the first prompt fails instead, as a `ResultError` from `query()` and `ask` and as a `ResultMessage` with `is_error` set on a `Client`. [Authentication Failures](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/errors.md#authentication-failures) describes what arrives, for missing credentials and for a rejected key.
|
|
85
61
|
|
|
86
62
|
## Quick Start
|
|
87
63
|
|
|
@@ -100,7 +76,7 @@ end
|
|
|
100
76
|
puts result
|
|
101
77
|
```
|
|
102
78
|
|
|
103
|
-
It raises the same errors as `query()` (see [docs/errors.md](https://github.com/
|
|
79
|
+
It raises the same errors as `query()` (see [docs/errors.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/errors.md)), plus `CLIConnectionError` if the stream ends without a result.
|
|
104
80
|
|
|
105
81
|
### `query()` — one-shot and streaming
|
|
106
82
|
|
|
@@ -159,7 +135,7 @@ ensure
|
|
|
159
135
|
end
|
|
160
136
|
```
|
|
161
137
|
|
|
162
|
-
See [docs/client.md](https://github.com/
|
|
138
|
+
See [docs/client.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/client.md) for `interrupt`, mid-session model and permission switching, MCP status, and custom transports.
|
|
163
139
|
|
|
164
140
|
### Custom tools (SDK MCP servers)
|
|
165
141
|
|
|
@@ -176,54 +152,100 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
|
176
152
|
mcp_servers: { tools: server },
|
|
177
153
|
allowed_tools: ['mcp__tools__greet']
|
|
178
154
|
)
|
|
155
|
+
|
|
156
|
+
puts ClaudeAgentSDK.ask('Greet Alice with the greet tool', options: options).result
|
|
179
157
|
```
|
|
180
158
|
|
|
181
|
-
A String return is sent to Claude as a single text block. Return a Hash instead (`{ content: [...], is_error: true }`) to flag an error, attach `structured_content:`, or send several content blocks or images. Arguments are validated against the tool's JSON Schema before your handler runs, and handler exceptions are reported back to the model in-band so it can self-correct. See [docs/mcp-servers.md](https://github.com/
|
|
159
|
+
A String return is sent to Claude as a single text block. Return a Hash instead (`{ content: [...], is_error: true }`) to flag an error, attach `structured_content:`, or send several content blocks or images. Arguments are validated against the tool's JSON Schema before your handler runs, and handler exceptions are reported back to the model in-band so it can self-correct. See [docs/mcp-servers.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/mcp-servers.md) for resources, prompts, mixed SDK + external servers, and schema details.
|
|
182
160
|
|
|
183
161
|
### Hooks and permission callbacks
|
|
184
162
|
|
|
185
|
-
Hooks run your Ruby code at any of the
|
|
163
|
+
Hooks run your Ruby code at any of the 33 lifecycle events (`PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `PreCompact`, …) with typed inputs. Permission callbacks decide programmatically whether a tool call may proceed.
|
|
186
164
|
|
|
187
165
|
```ruby
|
|
166
|
+
# A PreToolUse hook: refuse any Bash command that contains `rm -rf`.
|
|
167
|
+
block_rm_rf = lambda do |input, _tool_use_id, _context|
|
|
168
|
+
next {} unless input.tool_input[:command].to_s.include?('rm -rf')
|
|
169
|
+
|
|
170
|
+
{ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny',
|
|
171
|
+
permissionDecisionReason: 'rm -rf is not allowed' } }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# A permission callback: asked before a tool call that no rule already allows.
|
|
175
|
+
protect_env_files = lambda do |tool_name, input, _context|
|
|
176
|
+
if tool_name == 'Write' && input[:file_path].to_s.end_with?('.env')
|
|
177
|
+
ClaudeAgentSDK::PermissionResultDeny.new(message: '.env files are off limits')
|
|
178
|
+
else
|
|
179
|
+
ClaudeAgentSDK::PermissionResultAllow.new
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
188
183
|
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
189
|
-
hooks: { 'PreToolUse' => [ClaudeAgentSDK::HookMatcher.new(matcher: 'Bash', hooks: [
|
|
190
|
-
can_use_tool:
|
|
184
|
+
hooks: { 'PreToolUse' => [ClaudeAgentSDK::HookMatcher.new(matcher: 'Bash', hooks: [block_rm_rf])] },
|
|
185
|
+
can_use_tool: protect_env_files
|
|
191
186
|
)
|
|
192
187
|
```
|
|
193
188
|
|
|
194
|
-
See [docs/hooks-and-permissions.md](https://github.com/
|
|
189
|
+
See [docs/hooks-and-permissions.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/hooks-and-permissions.md) for the full event list and worked examples.
|
|
190
|
+
|
|
191
|
+
## Rails in a minute
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
bundle add claude-agent-sdk
|
|
195
|
+
bin/rails generate claude_agent_sdk:install # config/initializers/claude_agent_sdk.rb + .gitignore entry
|
|
196
|
+
bin/rails claude_agent_sdk:install_cli # the tested CLI into vendor/claude (also a Docker build step)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
# app/jobs/summarize_ticket_job.rb
|
|
201
|
+
class SummarizeTicketJob < ApplicationJob
|
|
202
|
+
def perform(ticket)
|
|
203
|
+
options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
204
|
+
tools: [], max_turns: 1, # text only, no built-in tools
|
|
205
|
+
env: { 'CLAUDE_CODE_DISABLE_AUTO_MEMORY' => '1' } # keeps one user's "remember…" out of the next user's session
|
|
206
|
+
)
|
|
207
|
+
prompt = "Summarize this support ticket in two sentences:\n\n#{ticket.body}"
|
|
208
|
+
|
|
209
|
+
ClaudeAgentSDK.query(prompt: prompt, options: options) do |message|
|
|
210
|
+
ticket.update!(summary: message.result) if message.is_a?(ClaudeAgentSDK::ResultMessage)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The block runs on a plain thread, so ActiveRecord calls inside it are safe. It is a thread of its own, though: the job's `Current` attributes, time zone, log tags and database role or shard are not set there (see [Request state does not follow into callbacks](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/rails.md#request-state-does-not-follow-into-callbacks)). [docs/rails.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/rails.md) continues with multi-turn sessions, ActionCable streaming, per-user isolation, and fiber workers.
|
|
195
217
|
|
|
196
218
|
## Documentation
|
|
197
219
|
|
|
198
220
|
| Topic | Guide |
|
|
199
221
|
|-------|-------|
|
|
200
|
-
| `Client` advanced features and custom transports | [docs/client.md](https://github.com/
|
|
201
|
-
| SDK MCP servers: tools, resources, prompts, schema compatibility | [docs/mcp-servers.md](https://github.com/
|
|
202
|
-
| All hook events, typed inputs, permission callbacks | [docs/hooks-and-permissions.md](https://github.com/
|
|
203
|
-
| Structured output, thinking, budget, fallback and advisor models, sandbox, bare mode, session isolation, checkpointing | [docs/configuration.md](https://github.com/
|
|
204
|
-
| Every `ClaudeAgentOptions` attribute: type, default, and the CLI flag or protocol field it becomes; environment variables | [docs/options.md](https://github.com/
|
|
205
|
-
| Session listing, reading, renaming, tagging, forking, resume-at-message | [docs/sessions.md](https://github.com/
|
|
206
|
-
| Subagent capabilities, event contracts, and minimal example | [docs/subagents.md](https://github.com/
|
|
207
|
-
| OpenTelemetry tracing, Langfuse, custom observers | [docs/observability.md](https://github.com/
|
|
208
|
-
| Rails: generator, `install_cli` task, callback wrapper, fiber safety, solid_queue fiber workers, ActionCable, jobs | [docs/rails.md](https://github.com/
|
|
209
|
-
| Vendoring a pinned CLI binary and CLI discovery order | [docs/cli-installer.md](https://github.com/
|
|
210
|
-
| Hash-key rule, attribute access, and the message, content block, and configuration type reference | [docs/types.md](https://github.com/
|
|
211
|
-
| Error handling, exception hierarchy, timeouts | [docs/errors.md](https://github.com/
|
|
212
|
-
|
|
213
|
-
API reference: [rubydoc.info/gems/claude-agent-sdk](https://rubydoc.info/gems/claude-agent-sdk). Available built-in tools: [Claude Code
|
|
222
|
+
| `Client` advanced features and custom transports | [docs/client.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/client.md) |
|
|
223
|
+
| SDK MCP servers: tools, resources, prompts, schema compatibility | [docs/mcp-servers.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/mcp-servers.md) |
|
|
224
|
+
| All hook events, typed inputs, permission callbacks | [docs/hooks-and-permissions.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/hooks-and-permissions.md) |
|
|
225
|
+
| Structured output, thinking, budget, fallback and advisor models, sandbox, bare mode, session isolation, checkpointing | [docs/configuration.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/configuration.md) |
|
|
226
|
+
| Every `ClaudeAgentOptions` attribute: type, default, and the CLI flag or protocol field it becomes; environment variables | [docs/options.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/options.md) |
|
|
227
|
+
| Session listing, reading, renaming, tagging, forking, resume-at-message | [docs/sessions.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/sessions.md) |
|
|
228
|
+
| Subagent capabilities, event contracts, and minimal example | [docs/subagents.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/subagents.md) |
|
|
229
|
+
| OpenTelemetry tracing, Langfuse, custom observers | [docs/observability.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/observability.md) |
|
|
230
|
+
| Rails: generator, `install_cli` task, callback wrapper, fiber safety, solid_queue fiber workers, ActionCable, jobs | [docs/rails.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/rails.md) |
|
|
231
|
+
| Vendoring a pinned CLI binary and CLI discovery order | [docs/cli-installer.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/cli-installer.md) |
|
|
232
|
+
| Hash-key rule, attribute access, and the message, content block, and configuration type reference | [docs/types.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/types.md) |
|
|
233
|
+
| Error handling, exception hierarchy, timeouts | [docs/errors.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/docs/errors.md) |
|
|
234
|
+
|
|
235
|
+
API reference: [rubydoc.info/gems/claude-agent-sdk](https://rubydoc.info/gems/claude-agent-sdk). Available built-in tools: [Claude Code tools reference](https://code.claude.com/docs/en/tools-reference).
|
|
214
236
|
|
|
215
237
|
## Examples
|
|
216
238
|
|
|
217
|
-
Runnable scripts live in [`examples/`](https://github.com/
|
|
239
|
+
Runnable scripts live in [`examples/`](https://github.com/rubycatco/claude-agent-sdk-ruby/tree/main/examples).
|
|
218
240
|
|
|
219
241
|
| Area | Examples |
|
|
220
242
|
|------|----------|
|
|
221
|
-
| Getting started | [quick_start](https://github.com/
|
|
222
|
-
| Sessions and output | [session_resumption](https://github.com/
|
|
223
|
-
| Tools and MCP | [mcp_calculator](https://github.com/
|
|
224
|
-
| Hooks and permissions | [hooks](https://github.com/
|
|
225
|
-
| Models and limits | [budget_control](https://github.com/
|
|
226
|
-
| Rails, observability, transports | [rails_actioncable](https://github.com/
|
|
243
|
+
| Getting started | [quick_start](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/quick_start.rb) · [client](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/client_example.rb) · [streaming_input](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/streaming_input_example.rb) · [message_types](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/message_types_example.rb) · [error_handling](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/error_handling_example.rb) |
|
|
244
|
+
| Sessions, subagents and output | [session_resumption](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/session_resumption_example.rb) · [subagent_status](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/subagent_status_example.rb) · [structured_output](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/structured_output_example.rb) · [extended_thinking](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/extended_thinking_example.rb) · [session_stores/](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/session_stores/README.md) |
|
|
245
|
+
| Tools and MCP | [mcp_calculator](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/mcp_calculator.rb) · [mcp_resources_prompts](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/mcp_resources_prompts_example.rb) · [http_mcp_server](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/http_mcp_server_example.rb) |
|
|
246
|
+
| Hooks and permissions | [hooks](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/hooks_example.rb) · [advanced_hooks](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/advanced_hooks_example.rb) · [lifecycle_hooks](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/lifecycle_hooks_example.rb) · [permission_callback](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/permission_callback_example.rb) |
|
|
247
|
+
| Models and limits | [budget_control](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/budget_control_example.rb) · [fallback_model](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/fallback_model_example.rb) · [advisor](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/advisor_example.rb) · [bare_mode](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/bare_mode_example.rb) · [sandbox](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/sandbox_example.rb) |
|
|
248
|
+
| Rails, observability, transports | [rails_actioncable](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/rails_actioncable_example.rb) · [rails_background_job](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/rails_background_job_example.rb) · [otel_langfuse](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/otel_langfuse_example.rb) · [e2b_transport](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/e2b_transport_example.rb) |
|
|
227
249
|
|
|
228
250
|
## Comparison with the official SDKs
|
|
229
251
|
|
|
@@ -235,10 +257,10 @@ All three SDKs drive the same CLI over the same protocol, so capabilities line u
|
|
|
235
257
|
| Bidirectional `Client` | ✅ | ✅ | ✅ |
|
|
236
258
|
| Streaming input | `AsyncIterable` | `AsyncIterable` | `Enumerator` |
|
|
237
259
|
| Custom tools (SDK MCP servers) | `tool()` | `@tool` decorator | `create_tool` block |
|
|
238
|
-
| Hooks (all
|
|
260
|
+
| Hooks (all 33 events) | ✅ | 10 typed, the rest by name | ✅ |
|
|
239
261
|
| Permission callbacks | ✅ | ✅ | ✅ |
|
|
240
262
|
| Structured output | ✅ | ✅ | ✅ |
|
|
241
|
-
|
|
|
263
|
+
| Typed classes for every message type | ✅ | partial | ✅ |
|
|
242
264
|
| [Sandbox](https://github.com/anthropic-experimental/sandbox-runtime) settings | ✅ | partial | ✅ |
|
|
243
265
|
| Bare mode (`--bare`) | ✅ | via `extra_args` | ✅ |
|
|
244
266
|
| File checkpointing & rewind | ✅ | ✅ | ✅ |
|
|
@@ -249,14 +271,14 @@ All three SDKs drive the same CLI over the same protocol, so capabilities line u
|
|
|
249
271
|
| Custom transport (pluggable I/O) | — | ✅ | ✅ |
|
|
250
272
|
| Rails integration | — | — | ✅ |
|
|
251
273
|
|
|
252
|
-
Types are plain Ruby classes with `attr_accessor` and keyword arguments, mirroring the field names of the TypeScript Zod schemas and Python dataclasses; there is no runtime type checking.
|
|
274
|
+
Types are plain Ruby classes with `attr_accessor` and keyword arguments, mirroring the field names of the TypeScript Zod schemas and Python dataclasses; there is no runtime type checking. The gem ships RBS signatures for its public API in `sig/`, which Steep and other RBS tools pick up through `rbs collection`.
|
|
253
275
|
|
|
254
276
|
## Claude Code plugin
|
|
255
277
|
|
|
256
278
|
This repository is also a Claude Code plugin marketplace. The bundled skill teaches Claude Code the gem's APIs and patterns:
|
|
257
279
|
|
|
258
280
|
```bash
|
|
259
|
-
/plugin marketplace add
|
|
281
|
+
/plugin marketplace add rubycatco/claude-agent-sdk-ruby
|
|
260
282
|
/plugin install claude-agent-ruby@claude-agent-sdk-ruby
|
|
261
283
|
```
|
|
262
284
|
|
|
@@ -272,12 +294,12 @@ RUN_INTEGRATION=1 bundle exec rspec # also run the real-CLI integration suite (
|
|
|
272
294
|
BUNDLE_GEMFILE=gemfiles/rails_8.gemfile bundle exec rspec --options spec/rails/.rspec # Rails integration specs
|
|
273
295
|
```
|
|
274
296
|
|
|
275
|
-
CI runs the suite and RuboCop on Ruby 3.2
|
|
297
|
+
CI runs the suite and RuboCop on Ruby 3.2 through 4.0, the Rails specs on Rails 7.1 and 8, and both RBS tasks. See [CONTRIBUTING.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/CONTRIBUTING.md) for the development setup and the CI matrix, and [spec/README.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/spec/README.md) for the test layout.
|
|
276
298
|
|
|
277
299
|
## Contributing
|
|
278
300
|
|
|
279
|
-
Bug reports and pull requests are welcome on [GitHub](https://github.com/
|
|
301
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/rubycatco/claude-agent-sdk-ruby/issues). Please include a failing spec with bug reports where possible, and keep pull requests focused on one change; [CONTRIBUTING.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/CONTRIBUTING.md) has the details. Report security vulnerabilities privately, as described in [SECURITY.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/SECURITY.md). Releases follow [Semantic Versioning](https://semver.org/) and are recorded in the [CHANGELOG](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/CHANGELOG.md).
|
|
280
302
|
|
|
281
303
|
## License
|
|
282
304
|
|
|
283
|
-
Released under the [MIT License](https://github.com/
|
|
305
|
+
Released under the [MIT License](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/LICENSE).
|
data/UPGRADING-1.0.md
CHANGED
|
@@ -114,7 +114,7 @@ No breaking changes within 1.x to the public API: everything documented in
|
|
|
114
114
|
YARD docs that is not tagged `@api private`. `@api private` objects (`Query`,
|
|
115
115
|
`MessageParser`, `FiberBoundary`, the `Sessions*` modules, ...) stay callable
|
|
116
116
|
but can change in any release. See
|
|
117
|
-
[CONTRIBUTING.md](https://github.com/
|
|
117
|
+
[CONTRIBUTING.md](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/CONTRIBUTING.md#what-is-public-api).
|
|
118
118
|
|
|
119
119
|
A removal is first deprecated in a minor release with a one-time warning that
|
|
120
120
|
names the replacement, and happens in the next major.
|
data/docs/client.md
CHANGED
|
@@ -64,6 +64,8 @@ ClaudeAgentSDK::Client.open do |client|
|
|
|
64
64
|
end
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
To handle Ctrl-C yourself in a terminal app (call `interrupt`, ask, or ignore it) rather than have it stop the CLI too, see [`process_group`](options.md#process_group).
|
|
68
|
+
|
|
67
69
|
The Ruby-style names above sit next to the Python SDK's spellings, and both work, so code ported from the Python docs runs unchanged:
|
|
68
70
|
|
|
69
71
|
| Ruby style | Python-parity name |
|
|
@@ -158,7 +160,7 @@ Resuming from a `session_store` is not available over a custom transport, throug
|
|
|
158
160
|
|
|
159
161
|
### Reference: running `claude` inside an E2B sandbox
|
|
160
162
|
|
|
161
|
-
[`examples/e2b_transport_example.rb`](https://github.com/
|
|
163
|
+
[`examples/e2b_transport_example.rb`](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/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:
|
|
162
164
|
|
|
163
165
|
```
|
|
164
166
|
ClaudeAgentSDK::Client (host)
|
data/docs/configuration.md
CHANGED
|
@@ -37,7 +37,7 @@ end
|
|
|
37
37
|
|
|
38
38
|
In the `output_format` Hash, `type` may be a String or a Symbol (`type: :json_schema`), and the keys Symbols or Strings.
|
|
39
39
|
|
|
40
|
-
See [examples/structured_output_example.rb](https://github.com/
|
|
40
|
+
See [examples/structured_output_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/structured_output_example.rb).
|
|
41
41
|
|
|
42
42
|
## Thinking Configuration
|
|
43
43
|
|
|
@@ -117,7 +117,7 @@ ClaudeAgentSDK.query(prompt: "Explain recursion", options: options) do |message|
|
|
|
117
117
|
end
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
See [examples/budget_control_example.rb](https://github.com/
|
|
120
|
+
See [examples/budget_control_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/budget_control_example.rb).
|
|
121
121
|
|
|
122
122
|
## Fallback Model
|
|
123
123
|
|
|
@@ -128,7 +128,7 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
|
128
128
|
)
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
See [examples/fallback_model_example.rb](https://github.com/
|
|
131
|
+
See [examples/fallback_model_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/fallback_model_example.rb).
|
|
132
132
|
|
|
133
133
|
## Advisor Model
|
|
134
134
|
|
|
@@ -162,7 +162,7 @@ Notes:
|
|
|
162
162
|
- `CLAUDE_CODE_DISABLE_ADVISOR_TOOL=1` (settable via `options.env`) disables
|
|
163
163
|
the advisor tool entirely; a configured `advisor_model` is then ignored.
|
|
164
164
|
|
|
165
|
-
See [examples/advisor_example.rb](https://github.com/
|
|
165
|
+
See [examples/advisor_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/advisor_example.rb) and the
|
|
166
166
|
[advisor documentation](https://code.claude.com/docs/en/advisor).
|
|
167
167
|
|
|
168
168
|
## Beta Features
|
|
@@ -260,7 +260,7 @@ sandbox = ClaudeAgentSDK::SandboxSettings.new(enabled: true, fail_if_unavailable
|
|
|
260
260
|
|
|
261
261
|
Without it, the sign is the CLI's warning on stderr ("Sandbox disabled: ... Commands will run WITHOUT sandboxing. Network and filesystem restrictions will NOT be enforced."), and stderr reaches your code only through the `stderr` (or `debug_stderr`) option. This is the CLI's own description of its behavior: the fallback has not been reproduced in this SDK's testing, where the sandbox was always available.
|
|
262
262
|
|
|
263
|
-
See [examples/sandbox_example.rb](https://github.com/
|
|
263
|
+
See [examples/sandbox_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/sandbox_example.rb).
|
|
264
264
|
|
|
265
265
|
When the `sandbox:` option enabled the sandbox and the CLI reports `Sandbox disabled: …` on its stderr, the SDK repeats that line as a Ruby warning prefixed `[claude-agent-sdk]`, once per session, whether or not you set `stderr:`.
|
|
266
266
|
|
|
@@ -293,7 +293,7 @@ options = ClaudeAgentSDK::ClaudeAgentOptions.new(
|
|
|
293
293
|
|
|
294
294
|
**What still works:** skills (via `/skill-name`), explicit `--add-dir` CLAUDE.md, `--settings`, `--mcp-config`, `--agents`, `--plugin-dir`, API key from `ANTHROPIC_API_KEY` env var.
|
|
295
295
|
|
|
296
|
-
See [examples/bare_mode_example.rb](https://github.com/
|
|
296
|
+
See [examples/bare_mode_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/bare_mode_example.rb).
|
|
297
297
|
|
|
298
298
|
## Verbatim Prompts
|
|
299
299
|
|
data/docs/errors.md
CHANGED
|
@@ -18,7 +18,7 @@ ClaudeAgentSDK.query(prompt: "Hello") do |message|
|
|
|
18
18
|
end
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
See [examples/error_handling_example.rb](https://github.com/
|
|
21
|
+
See [examples/error_handling_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/error_handling_example.rb).
|
|
22
22
|
|
|
23
23
|
## Exception Handling
|
|
24
24
|
|
|
@@ -87,6 +87,32 @@ initial handshake that was still in flight when the CLI exited. Match on
|
|
|
87
87
|
`Resume rejected by --resume-drops-turn:` in the message and treat it as
|
|
88
88
|
deterministic: clear the fork target and resume plainly rather than retrying.
|
|
89
89
|
|
|
90
|
+
## Authentication Failures
|
|
91
|
+
|
|
92
|
+
The SDK holds no credentials; the CLI authenticates itself (see the README's
|
|
93
|
+
[Authentication](../README.md#authentication)). Neither of the two ways that
|
|
94
|
+
can fail is reported when the session starts.
|
|
95
|
+
|
|
96
|
+
**No credentials at all** (a fresh container, a CI runner). The first prompt
|
|
97
|
+
comes back as an `AssistantMessage` whose `error` is `'authentication_failed'`
|
|
98
|
+
(its text is "Not logged in · Please run /login", a command an SDK host cannot
|
|
99
|
+
run), followed by a `ResultMessage` with `is_error` set. `query()` and `ask`
|
|
100
|
+
then raise `ResultError` with `terminal_reason == 'api_error'`. A `Client`
|
|
101
|
+
session stays open, so check the result's `is_error` there.
|
|
102
|
+
|
|
103
|
+
**A key or token the API rejects.** It ends the same way, with
|
|
104
|
+
`api_error_status` 401, but only after the CLI has retried: ten
|
|
105
|
+
`APIRetryMessage`s over about three minutes when tested. Watch for them rather
|
|
106
|
+
than waiting for the error:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
ClaudeAgentSDK.query(prompt: "Hello") do |message|
|
|
110
|
+
next unless message.is_a?(ClaudeAgentSDK::APIRetryMessage)
|
|
111
|
+
|
|
112
|
+
warn "API retry #{message.attempt}/#{message.max_retries}: HTTP #{message.error_status} (#{message.error})"
|
|
113
|
+
end
|
|
114
|
+
```
|
|
115
|
+
|
|
90
116
|
## Errors from Client Control Methods
|
|
91
117
|
|
|
92
118
|
`Client#interrupt`, `#set_model`, `#set_permission_mode`, `#rewind_files`, `#reconnect_mcp_server`, `#toggle_mcp_server`, `#stop_task` and the other control methods (the Ruby-style `model=` and `permission_mode=` included) send a request to the CLI and wait for its answer. They can fail in three ways:
|
|
@@ -196,4 +222,4 @@ end
|
|
|
196
222
|
| `CLIJSONDecodeError` | JSON parsing issues — including stdout ending mid-frame (a truncated final message; `line` holds the partial frame) |
|
|
197
223
|
| `MessageParseError` | Message parsing issues |
|
|
198
224
|
|
|
199
|
-
See [lib/claude_agent_sdk/errors.rb](https://github.com/
|
|
225
|
+
See [lib/claude_agent_sdk/errors.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/lib/claude_agent_sdk/errors.rb) for all error types.
|
|
@@ -6,12 +6,14 @@ A **hook** is a Ruby proc/lambda that the Claude Code *application* (*not* Claud
|
|
|
6
6
|
|
|
7
7
|
### Supported Events
|
|
8
8
|
|
|
9
|
-
All hook input objects include common fields
|
|
9
|
+
All hook input objects include the common fields `session_id`, `transcript_path`, `cwd`, `permission_mode`, `prompt_id` (the UUID of the prompt being processed, also the `prompt.id` of the CLI's OpenTelemetry events) and `effort` (the turn's reasoning effort, e.g. `{ level: "high" }`, on models that support it), plus `raw_input`: the payload exactly as the CLI sent it, Symbol keys included. A field the CLI sends that this SDK version has no reader for is still in `raw_input`.
|
|
10
10
|
|
|
11
11
|
- `PreToolUse` → `PreToolUseHookInput` (`tool_name`, `tool_input`, `tool_use_id`)
|
|
12
|
-
- `PostToolUse` → `PostToolUseHookInput` (`tool_name`, `tool_input`, `tool_response`, `tool_use_id`)
|
|
13
|
-
- `PostToolUseFailure` → `PostToolUseFailureHookInput` (`tool_name`, `tool_input`, `tool_use_id`, `error`, `is_interrupt`)
|
|
14
|
-
- `
|
|
12
|
+
- `PostToolUse` → `PostToolUseHookInput` (`tool_name`, `tool_input`, `tool_response`, `tool_use_id`, `duration_ms`)
|
|
13
|
+
- `PostToolUseFailure` → `PostToolUseFailureHookInput` (`tool_name`, `tool_input`, `tool_use_id`, `error`, `is_interrupt`, `duration_ms`)
|
|
14
|
+
- `PostToolBatch` → `PostToolBatchHookInput` (`tool_calls`)
|
|
15
|
+
- `UserPromptSubmit` → `UserPromptSubmitHookInput` (`prompt`, `session_title`, `source`)
|
|
16
|
+
- `UserPromptExpansion` → `UserPromptExpansionHookInput` (`expansion_type`, `command_name`, `command_args`, `command_source`, `prompt`)
|
|
15
17
|
- `Stop` → `StopHookInput` (`stop_hook_active`, `last_assistant_message`, `background_tasks`, `session_crons`)
|
|
16
18
|
- `SubagentStop` → `SubagentStopHookInput` (`stop_hook_active`, `agent_id`, `agent_transcript_path`, `agent_type`, `last_assistant_message`, `background_tasks`, `session_crons`)
|
|
17
19
|
- `PreCompact` → `PreCompactHookInput` (`trigger`, `custom_instructions`)
|
|
@@ -21,7 +23,7 @@ All hook input objects include common fields like `session_id`, `transcript_path
|
|
|
21
23
|
|
|
22
24
|
`tool_input` (and the `input` a [permission callback](#permission-callbacks) receives) is the CLI's Hash passed through unchanged, so its keys are Symbols spelled as on the wire: `tool_input[:command]`, `input[:file_path]`. See [Hash keys](types.md#hash-keys).
|
|
23
25
|
|
|
24
|
-
All
|
|
26
|
+
All 33 hook events have typed input classes (an event this SDK version does not know arrives as an `UnknownHookInput`, with its payload in `raw_input`). See [`ClaudeAgentSDK::HOOK_EVENTS`](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/lib/claude_agent_sdk/types/hooks.rb) and [examples/lifecycle_hooks_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/lifecycle_hooks_example.rb).
|
|
25
27
|
|
|
26
28
|
`background_tasks` and `session_crons` are optional arrays of raw CLI hashes.
|
|
27
29
|
`nil` means the CLI did not provide a snapshot; `[]` means it provided an empty
|
|
@@ -76,7 +78,7 @@ Async do
|
|
|
76
78
|
end.wait
|
|
77
79
|
```
|
|
78
80
|
|
|
79
|
-
See [examples/hooks_example.rb](https://github.com/
|
|
81
|
+
See [examples/hooks_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/hooks_example.rb), [examples/advanced_hooks_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/advanced_hooks_example.rb), and [examples/lifecycle_hooks_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/lifecycle_hooks_example.rb).
|
|
80
82
|
|
|
81
83
|
### Hook output
|
|
82
84
|
|
|
@@ -97,6 +99,33 @@ A callback returns a typed output (`AsyncHookJSONOutput`, or `SyncHookJSONOutput
|
|
|
97
99
|
- The keys inside `updated_input`, `updated_tool_output`, `updated_mcp_tool_output` and a `PermissionRequest` `decision` are not renamed: those are the tool's own payloads (for `decision`, the CLI's), so spell them as the tool or the CLI does.
|
|
98
100
|
- If one Hash spells the same field both ways, the CLI's spelling is the one sent (`permissionDecision` over `permission_decision`, `continue` over `continue_`).
|
|
99
101
|
|
|
102
|
+
The typed `hook_specific_output` classes, and the fields each one sends (a field left `nil` is not sent):
|
|
103
|
+
|
|
104
|
+
| Class | Fields |
|
|
105
|
+
|-------|--------|
|
|
106
|
+
| `PreToolUseHookSpecificOutput` | `permission_decision`, `permission_decision_reason`, `updated_input`, `additional_context` |
|
|
107
|
+
| `PostToolUseHookSpecificOutput` | `additional_context`, `updated_tool_output`, `updated_mcp_tool_output` |
|
|
108
|
+
| `PostToolUseFailureHookSpecificOutput` | `additional_context` |
|
|
109
|
+
| `PostToolBatchHookSpecificOutput` | `additional_context` |
|
|
110
|
+
| `UserPromptSubmitHookSpecificOutput` | `additional_context`, `session_title`, `suppress_original_prompt` |
|
|
111
|
+
| `UserPromptExpansionHookSpecificOutput` | `additional_context`, `suppress_original_prompt` |
|
|
112
|
+
| `SessionStartHookSpecificOutput` | `additional_context`, `initial_user_message`, `session_title`, `watch_paths`, `reload_skills` |
|
|
113
|
+
| `SetupHookSpecificOutput` | `additional_context` |
|
|
114
|
+
| `NotificationHookSpecificOutput` | `additional_context` |
|
|
115
|
+
| `SubagentStartHookSpecificOutput` | `additional_context` |
|
|
116
|
+
| `StopHookSpecificOutput` | `additional_context` (feedback the model acts on; the conversation continues) |
|
|
117
|
+
| `SubagentStopHookSpecificOutput` | `additional_context` (the same, for the subagent) |
|
|
118
|
+
| `PermissionRequestHookSpecificOutput` | `decision` |
|
|
119
|
+
| `PermissionDeniedHookSpecificOutput` | `retry` |
|
|
120
|
+
| `PreModelSwitchHookSpecificOutput` | `permission_decision` (`"allow"` / `"deny"`), `permission_decision_reason` |
|
|
121
|
+
| `PostModelSwitchHookSpecificOutput` | `additional_context` |
|
|
122
|
+
| `ElicitationHookSpecificOutput` | `action` (`"accept"` / `"decline"` / `"cancel"`), `content` |
|
|
123
|
+
| `ElicitationResultHookSpecificOutput` | `action`, `content` |
|
|
124
|
+
| `CwdChangedHookSpecificOutput` | `watch_paths` |
|
|
125
|
+
| `FileChangedHookSpecificOutput` | `watch_paths` |
|
|
126
|
+
| `WorktreeCreateHookSpecificOutput` | `worktree_path` |
|
|
127
|
+
| `MessageDisplayHookSpecificOutput` | `display_content` (changes the screen only, not the stored message) |
|
|
128
|
+
|
|
100
129
|
### Hook cancellation
|
|
101
130
|
|
|
102
131
|
Dispatched hooks receive `HookContext#request_id` and `HookContext#signal`, using
|
|
@@ -148,7 +177,7 @@ Async do
|
|
|
148
177
|
end.wait
|
|
149
178
|
```
|
|
150
179
|
|
|
151
|
-
See [examples/permission_callback_example.rb](https://github.com/
|
|
180
|
+
See [examples/permission_callback_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/permission_callback_example.rb).
|
|
152
181
|
|
|
153
182
|
### Permission request cancellation
|
|
154
183
|
|
data/docs/mcp-servers.md
CHANGED
|
@@ -222,4 +222,4 @@ are covered here too. For
|
|
|
222
222
|
naming the exception class, and then the exception propagates as Ruby normally
|
|
223
223
|
would.
|
|
224
224
|
|
|
225
|
-
See [examples/mcp_calculator.rb](https://github.com/
|
|
225
|
+
See [examples/mcp_calculator.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/mcp_calculator.rb) and [examples/mcp_resources_prompts_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/mcp_resources_prompts_example.rb) for complete examples.
|
data/docs/observability.md
CHANGED
|
@@ -204,4 +204,4 @@ end
|
|
|
204
204
|
options = ClaudeAgentSDK::ClaudeAgentOptions.new(observers: [MyObserver.new])
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
-
See [examples/otel_langfuse_example.rb](https://github.com/
|
|
207
|
+
See [examples/otel_langfuse_example.rb](https://github.com/rubycatco/claude-agent-sdk-ruby/blob/main/examples/otel_langfuse_example.rb) for a complete multi-tool example.
|
data/docs/options.md
CHANGED
|
@@ -170,6 +170,7 @@ Guides: [Vendoring the CLI](cli-installer.md), [Custom Transport](client.md#cust
|
|
|
170
170
|
| `cwd` | `String \| Pathname` | `nil` | The working directory of the CLI process (and its `PWD`) |
|
|
171
171
|
| `env` | `Hash[String \| Symbol, String?]` | `{}` | Merged over the environment the CLI process inherits. A `nil` value unsets the variable |
|
|
172
172
|
| `user` | `String \| Integer` | `nil` | The user the CLI process runs as (name or uid; Unix, and the SDK process needs the privilege to switch) |
|
|
173
|
+
| `process_group` | `bool` | `false`; explicit `nil` stays `nil` | Nothing (SDK only): the CLI process starts in a process group of its own, out of reach of the terminal's Ctrl-C. See [`process_group`](#process_group) |
|
|
173
174
|
| `extra_args` | `Hash[String \| Symbol, untyped]` | `{}` | `--<flag> <value>` per pair, `--<flag>` for a `nil` value. See [`extra_args`](#extra_args) |
|
|
174
175
|
| `stderr` | `_StderrCallback` | `nil` | Nothing (SDK only): called with each line the CLI writes to stderr |
|
|
175
176
|
| `debug_stderr` | `_Puts \| String` | `nil` | Nothing (SDK only): an IO-like object (`puts`) or a file path that receives each CLI stderr line |
|
|
@@ -177,6 +178,48 @@ Guides: [Vendoring the CLI](cli-installer.md), [Custom Transport](client.md#cust
|
|
|
177
178
|
|
|
178
179
|
These options describe the process the SDK starts itself. A [custom transport](client.md#custom-transport) starts the CLI its own way and decides what to do with them.
|
|
179
180
|
|
|
181
|
+
### `process_group`
|
|
182
|
+
|
|
183
|
+
In a terminal, Ctrl-C sends SIGINT to every process of the foreground process group. The CLI is in your process's group, so it gets the signal at the same moment your process does and ends the turn on its own, before your code can decide what to do: interrupt and keep the partial reply, ask whether to stop, or ignore the keypress. With `process_group: true` the CLI process the SDK starts for the session begins in a process group of its own, and the terminal's SIGINT no longer reaches it, nor the processes it starts that stay in its group (Bash tool commands, stdio MCP servers). The short `claude -v` version check that runs before it stays in your group.
|
|
184
|
+
|
|
185
|
+
It applies to `query()` and `Client` alike, on the default transport. A [custom transport](client.md#custom-transport) starts the CLI its own way. A transport subclass whose `#build_command` wraps the CLI in another program (`docker exec`, `ssh`) puts that local program in a group of its own; the option does not set the process group of a CLI on the far side. The Python SDK has no equivalent; the TypeScript SDK leaves it to a custom `spawnClaudeCodeProcess`.
|
|
186
|
+
|
|
187
|
+
Your process now owns Ctrl-C. To stop a turn, call `Client#interrupt`, or `Client#disconnect` to end the session; a one-shot `query()` has no handle to interrupt. Neither may be called inside a signal trap: they take a mutex, and Ruby raises `ThreadError` (`can't be called from trap context`). Let the trap only record the signal, and make the call from ordinary code:
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
interrupts = Thread::Queue.new
|
|
191
|
+
previous = trap('INT') { interrupts << :int } # nothing else in here: no SDK calls
|
|
192
|
+
begin
|
|
193
|
+
options = ClaudeAgentSDK::ClaudeAgentOptions.new(process_group: true)
|
|
194
|
+
ClaudeAgentSDK::Client.open(options: options) do |client|
|
|
195
|
+
watcher = Thread.new do
|
|
196
|
+
client.interrupt if interrupts.pop # on this thread, outside the trap
|
|
197
|
+
end
|
|
198
|
+
client.query('Refactor the parser')
|
|
199
|
+
client.receive_response { |message| puts message }
|
|
200
|
+
ensure
|
|
201
|
+
interrupts << nil # ends the watcher; the queue stays open for the trap
|
|
202
|
+
watcher&.join
|
|
203
|
+
end
|
|
204
|
+
ensure
|
|
205
|
+
trap('INT', previous) # Ctrl-C behaves as before once the session is over
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
A plain script that does not trap INT is no worse off: Ctrl-C still raises `Interrupt` in your process, and the SDK's teardown stops the CLI as it does on any other exception.
|
|
210
|
+
|
|
211
|
+
When the CLI process stops:
|
|
212
|
+
|
|
213
|
+
- On `Client#disconnect`, or at the end of a `Client.open` block or a `query()` call, the usual teardown runs: the SDK closes the CLI's stdin, gives it a grace period to finish, then sends SIGTERM and finally SIGKILL.
|
|
214
|
+
- When the interpreter exits normally without a disconnect, an `at_exit` handler sends SIGTERM to the CLI process. Best effort: it does not wait for the turn.
|
|
215
|
+
- When your process dies abruptly (`exit!`, SIGKILL), the SDK cleans up nothing. The CLI sees its stdin close, which normally lets it wind down; how soon is up to the CLI and its background work (see [`CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS`](#claude_code_print_bg_wait_ceiling_ms)).
|
|
216
|
+
|
|
217
|
+
In every case the SDK signals the CLI process only, not its group. The processes the CLI starts are its own to stop, and with the option on they no longer get the terminal's SIGINT either.
|
|
218
|
+
|
|
219
|
+
The CLI's group is a background group of the terminal. A process in it that reads from the controlling terminal (a tool command that prompts on the tty) is stopped with SIGTTIN, and so is the rest of the group, the CLI included when the tool shares its group. Whether writing to the terminal stops it too depends on the terminal's TOSTOP setting. The SDK's own traffic with the CLI goes through pipes and is unaffected.
|
|
220
|
+
|
|
221
|
+
On Windows the option starts the CLI with `new_pgroup: true` (`CREATE_NEW_PROCESS_GROUP`), which also turns Ctrl-C off for the CLI process. It has not been exercised on Windows.
|
|
222
|
+
|
|
180
223
|
### `extra_args`
|
|
181
224
|
|
|
182
225
|
The way to pass a CLI flag the SDK has no option for. A key is the flag's name without the leading dashes; the value follows it, and `nil` means a flag without a value:
|