claude_agent 0.1.0 → 0.3.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 +16 -0
- data/README.md +87 -2
- data/SPEC.md +22 -14
- data/lib/claude_agent/hooks.rb +4 -2
- data/lib/claude_agent/options.rb +5 -2
- data/lib/claude_agent/permissions.rb +18 -10
- data/lib/claude_agent/session.rb +207 -0
- data/lib/claude_agent/types.rb +5 -3
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +1 -0
- data/sig/claude_agent.rbs +46 -5
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c630af2842675c8aa126588105617fa1af823c437ef1095029b3f7e9efbc982c
|
|
4
|
+
data.tar.gz: 16199aa31e03d542bf9128356594a0ea6659a16ecfba7fa2b3e1a97429662ef2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f555706af9480e572d8f310a73ab0d1294b9eaf6b162146364032450bbad86f0a2e962649f12e74e48dae4155fd1929221efd46a349253d1bf96ad09249db54
|
|
7
|
+
data.tar.gz: fbf3a236589cad360dbfff15dfa8164a353a973e211e5f4ab4802d8b383d803359a137a9a312478bbd0175f7b866c4f40795f55c6d8b2eb8f028992f403b5182
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-01-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `agent` option for specifying main thread agent name (TypeScript SDK v0.2.9 parity)
|
|
14
|
+
- `model` field in `SessionStartInput` hook input
|
|
15
|
+
|
|
16
|
+
## [0.2.0] - 2026-01-11
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- V2 Session API for multi-turn conversations (`unstable_v2_create_session`, `unstable_v2_resume_session`, `unstable_v2_prompt`)
|
|
20
|
+
- `Session` class for stateful conversation management
|
|
21
|
+
- `SessionOptions` data type for V2 API configuration
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- `Options#initialize` now correctly handles nil values without overriding defaults
|
|
25
|
+
|
|
10
26
|
## [0.1.0] - 2026-01-10
|
|
11
27
|
|
|
12
28
|
### Added
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ClaudeAgent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Ruby gem for building AI-powered applications with the [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview). This library essentially wraps the Claude Code CLI, providing both simple one-shot queries and interactive bidirectional sessions.
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
@@ -95,6 +95,9 @@ options = ClaudeAgent::Options.new(
|
|
|
95
95
|
cwd: "/path/to/project",
|
|
96
96
|
add_dirs: ["/additional/path"],
|
|
97
97
|
|
|
98
|
+
# Agent configuration
|
|
99
|
+
agent: "my-agent", # Agent name for main thread
|
|
100
|
+
|
|
98
101
|
# Session management
|
|
99
102
|
resume: "session-id",
|
|
100
103
|
continue_conversation: true,
|
|
@@ -456,7 +459,7 @@ All available hook events:
|
|
|
456
459
|
| PostToolUseFailure | `PostToolUseFailureInput` | tool_name, tool_input, error, tool_use_id, is_interrupt |
|
|
457
460
|
| Notification | `NotificationInput` | message, title, notification_type |
|
|
458
461
|
| UserPromptSubmit | `UserPromptSubmitInput` | prompt |
|
|
459
|
-
| SessionStart | `SessionStartInput` | source, agent_type
|
|
462
|
+
| SessionStart | `SessionStartInput` | source, agent_type, model |
|
|
460
463
|
| SessionEnd | `SessionEndInput` | reason |
|
|
461
464
|
| Stop | `StopInput` | stop_hook_active |
|
|
462
465
|
| SubagentStart | `SubagentStartInput` | agent_id, agent_type |
|
|
@@ -591,6 +594,88 @@ puts client.account_info.email
|
|
|
591
594
|
client.disconnect
|
|
592
595
|
```
|
|
593
596
|
|
|
597
|
+
## V2 Session API (Unstable)
|
|
598
|
+
|
|
599
|
+
> **⚠️ Alpha API**: This API is unstable and may change without notice.
|
|
600
|
+
|
|
601
|
+
The V2 Session API provides a simpler interface for multi-turn conversations, matching the TypeScript SDK's `SDKSession` interface.
|
|
602
|
+
|
|
603
|
+
### Create a Session
|
|
604
|
+
|
|
605
|
+
```ruby
|
|
606
|
+
# Create a new session
|
|
607
|
+
session = ClaudeAgent.unstable_v2_create_session(
|
|
608
|
+
model: "claude-sonnet-4-5-20250514",
|
|
609
|
+
permission_mode: "acceptEdits"
|
|
610
|
+
)
|
|
611
|
+
|
|
612
|
+
# Send a message
|
|
613
|
+
session.send("Hello, Claude!")
|
|
614
|
+
|
|
615
|
+
# Stream responses
|
|
616
|
+
session.stream.each do |msg|
|
|
617
|
+
case msg
|
|
618
|
+
when ClaudeAgent::AssistantMessage
|
|
619
|
+
puts msg.text
|
|
620
|
+
when ClaudeAgent::ResultMessage
|
|
621
|
+
puts "Done! Cost: $#{msg.total_cost_usd}"
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
# Continue the conversation
|
|
626
|
+
session.send("Tell me more")
|
|
627
|
+
session.stream.each { |msg| puts msg.text if msg.is_a?(ClaudeAgent::AssistantMessage) }
|
|
628
|
+
|
|
629
|
+
# Close when done
|
|
630
|
+
session.close
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Resume a Session
|
|
634
|
+
|
|
635
|
+
```ruby
|
|
636
|
+
# Resume an existing session by ID
|
|
637
|
+
session = ClaudeAgent.unstable_v2_resume_session(
|
|
638
|
+
"session-abc123",
|
|
639
|
+
model: "claude-sonnet-4-5-20250514"
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
session.send("What were we discussing?")
|
|
643
|
+
session.stream.each { |msg| puts msg.text if msg.is_a?(ClaudeAgent::AssistantMessage) }
|
|
644
|
+
session.close
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### One-Shot Prompt
|
|
648
|
+
|
|
649
|
+
```ruby
|
|
650
|
+
# Simple one-shot prompt (auto-closes session)
|
|
651
|
+
result = ClaudeAgent.unstable_v2_prompt(
|
|
652
|
+
"What is 2 + 2?",
|
|
653
|
+
model: "claude-sonnet-4-5-20250514"
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
puts "Success: #{result.success?}"
|
|
657
|
+
puts "Cost: $#{result.total_cost_usd}"
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
### SessionOptions
|
|
661
|
+
|
|
662
|
+
The V2 API uses a simplified options type:
|
|
663
|
+
|
|
664
|
+
```ruby
|
|
665
|
+
options = ClaudeAgent::SessionOptions.new(
|
|
666
|
+
model: "claude-sonnet-4-5-20250514", # Required
|
|
667
|
+
permission_mode: "acceptEdits", # Optional
|
|
668
|
+
allowed_tools: ["Read", "Grep"], # Optional
|
|
669
|
+
disallowed_tools: ["Write"], # Optional
|
|
670
|
+
can_use_tool: ->(name, input, ctx) { ... }, # Optional
|
|
671
|
+
hooks: { "PreToolUse" => [...] }, # Optional
|
|
672
|
+
env: { "MY_VAR" => "value" }, # Optional
|
|
673
|
+
path_to_claude_code_executable: "/custom/path" # Optional
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
session = ClaudeAgent.unstable_v2_create_session(options)
|
|
677
|
+
```
|
|
678
|
+
|
|
594
679
|
## Types Reference
|
|
595
680
|
|
|
596
681
|
### Return Types
|
data/SPEC.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
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:
|
|
6
|
+
- TypeScript SDK: v0.2.9 (npm package)
|
|
7
|
+
- Python SDK: v0.1.20 from GitHub (commit 04da88d)
|
|
8
8
|
- Ruby SDK: This repository
|
|
9
9
|
|
|
10
10
|
---
|
|
@@ -64,13 +64,14 @@ Configuration options for SDK queries and clients.
|
|
|
64
64
|
| `sandbox` | ✅ | ✅ | ✅ | Sandbox settings |
|
|
65
65
|
| `settingSources` | ✅ | ✅ | ✅ | Which settings to load |
|
|
66
66
|
| `plugins` | ✅ | ✅ | ✅ | Plugin configurations |
|
|
67
|
-
| `betas` | ✅ | ✅ | ✅ | Beta features (e.g., context-1m)
|
|
67
|
+
| `betas` | ✅ | ✅ | ✅ | Beta features (e.g., context-1m-2025-08-07) |
|
|
68
|
+
| `agent` | ✅ | ❌ | ✅ | Agent name for main thread |
|
|
68
69
|
| `abortController` | ✅ | ❌ | ✅ | Cancellation controller |
|
|
69
70
|
| `stderr` | ✅ | ✅ | ✅ | Stderr callback |
|
|
70
71
|
| `spawnClaudeCodeProcess` | ✅ | ❌ | ✅ | Custom spawn function |
|
|
71
72
|
| `pathToClaudeCodeExecutable` | ✅ | ✅ | ✅ | Custom CLI path |
|
|
72
|
-
| `executable` | ✅ |
|
|
73
|
-
| `executableArgs` | ✅ |
|
|
73
|
+
| `executable` | ✅ | N/A | N/A | JS runtime (node/bun/deno) - JS-specific |
|
|
74
|
+
| `executableArgs` | ✅ | N/A | N/A | JS runtime args - JS-specific |
|
|
74
75
|
| `extraArgs` | ✅ | ✅ | ✅ | Extra CLI arguments |
|
|
75
76
|
| `user` | ❌ | ✅ | ✅ | User identifier |
|
|
76
77
|
|
|
@@ -289,7 +290,7 @@ Permission handling and updates.
|
|
|
289
290
|
| `updatedPermissions` | ✅ | ✅ | ✅ |
|
|
290
291
|
| `message` (deny) | ✅ | ✅ | ✅ |
|
|
291
292
|
| `interrupt` (deny) | ✅ | ✅ | ✅ |
|
|
292
|
-
| `toolUseID` | ✅ | ❌ |
|
|
293
|
+
| `toolUseID` | ✅ | ❌ | ✅ |
|
|
293
294
|
|
|
294
295
|
### Permission Update Types
|
|
295
296
|
|
|
@@ -316,7 +317,7 @@ Permission handling and updates.
|
|
|
316
317
|
|
|
317
318
|
| Field | TypeScript | Python | Ruby | Notes |
|
|
318
319
|
|------------------|:----------:|:------:|:----:|---------------------------|
|
|
319
|
-
| `signal` | ✅ | ✅ |
|
|
320
|
+
| `signal` | ✅ | ✅ | ✅ | Abort signal |
|
|
320
321
|
| `suggestions` | ✅ | ✅ | ✅ | Permission suggestions |
|
|
321
322
|
| `blockedPath` | ✅ | ✅ | ✅ | Blocked file path |
|
|
322
323
|
| `decisionReason` | ✅ | ❌ | ✅ | Why permission triggered |
|
|
@@ -392,10 +393,10 @@ Session management and resumption.
|
|
|
392
393
|
|
|
393
394
|
| Feature | TypeScript | Python | Ruby | Notes |
|
|
394
395
|
|-----------------------------|:----------:|:------:|:----:|---------------------------|
|
|
395
|
-
| `SDKSession` interface | ✅ | ❌ |
|
|
396
|
-
| `unstable_v2_createSession` | ✅ | ❌ |
|
|
397
|
-
| `unstable_v2_resumeSession` | ✅ | ❌ |
|
|
398
|
-
| `unstable_v2_prompt` | ✅ | ❌ |
|
|
396
|
+
| `SDKSession` interface | ✅ | ❌ | ✅ | Multi-turn session object |
|
|
397
|
+
| `unstable_v2_createSession` | ✅ | ❌ | ✅ | Create new session |
|
|
398
|
+
| `unstable_v2_resumeSession` | ✅ | ❌ | ✅ | Resume existing session |
|
|
399
|
+
| `unstable_v2_prompt` | ✅ | ❌ | ✅ | One-shot prompt |
|
|
399
400
|
|
|
400
401
|
---
|
|
401
402
|
|
|
@@ -531,6 +532,7 @@ Public API surface for SDK clients.
|
|
|
531
532
|
|
|
532
533
|
- ✅ = Fully implemented
|
|
533
534
|
- ❌ = Not implemented
|
|
535
|
+
- N/A = Not applicable (language-specific feature)
|
|
534
536
|
- Partial = Partially implemented
|
|
535
537
|
|
|
536
538
|
---
|
|
@@ -541,18 +543,24 @@ Public API surface for SDK clients.
|
|
|
541
543
|
- Primary reference for API surface (most comprehensive)
|
|
542
544
|
- Source is bundled/minified, but `sdk.d.ts` provides complete type definitions
|
|
543
545
|
- Includes unstable V2 session API
|
|
544
|
-
- Version 0.2.
|
|
546
|
+
- Version 0.2.9 adds `agent` option for specifying main thread agent
|
|
547
|
+
- Adds `deno` as supported executable option
|
|
548
|
+
- Includes experimental `criticalSystemReminder_EXPERIMENTAL` for agent definitions
|
|
549
|
+
- `SessionStartHookInput` includes `model` field
|
|
545
550
|
|
|
546
551
|
### Python SDK
|
|
547
|
-
- Full source available
|
|
552
|
+
- Full source available (v0.1.20)
|
|
548
553
|
- Fewer control protocol features than TypeScript
|
|
549
554
|
- Does not support SessionStart/SessionEnd/Notification hooks due to setup limitations
|
|
550
555
|
- Missing several permission modes (delegate, dontAsk)
|
|
551
556
|
- `excludedCommands` in sandbox now supported
|
|
557
|
+
- `tool_use_id` now included in PreToolUseHookInput
|
|
552
558
|
|
|
553
559
|
### Ruby SDK (This Repository)
|
|
554
|
-
-
|
|
560
|
+
- Full TypeScript SDK feature parity achieved
|
|
555
561
|
- Ruby-idiomatic patterns (Data.define, snake_case)
|
|
556
562
|
- Complete control protocol support
|
|
557
563
|
- Dedicated Client class for multi-turn conversations
|
|
558
564
|
- Full hook event support including all 12 events
|
|
565
|
+
- Full V2 Session API support (unstable)
|
|
566
|
+
- `executable`/`executableArgs` marked N/A (JS runtime options not applicable to Ruby)
|
data/lib/claude_agent/hooks.rb
CHANGED
|
@@ -141,14 +141,16 @@ module ClaudeAgent
|
|
|
141
141
|
# Input for SessionStart hook (TypeScript SDK parity)
|
|
142
142
|
#
|
|
143
143
|
class SessionStartInput < BaseHookInput
|
|
144
|
-
attr_reader :source, :agent_type
|
|
144
|
+
attr_reader :source, :agent_type, :model
|
|
145
145
|
|
|
146
146
|
# @param source [String] One of: "startup", "resume", "clear", "compact"
|
|
147
147
|
# @param agent_type [String, nil] Type of agent if running in subagent context
|
|
148
|
-
|
|
148
|
+
# @param model [String, nil] Model being used for this session
|
|
149
|
+
def initialize(source:, agent_type: nil, model: nil, **kwargs)
|
|
149
150
|
super(hook_event_name: "SessionStart", **kwargs)
|
|
150
151
|
@source = source
|
|
151
152
|
@agent_type = agent_type
|
|
153
|
+
@model = model
|
|
152
154
|
end
|
|
153
155
|
end
|
|
154
156
|
|
data/lib/claude_agent/options.rb
CHANGED
|
@@ -50,7 +50,7 @@ module ClaudeAgent
|
|
|
50
50
|
continue_conversation resume fork_session resume_session_at
|
|
51
51
|
max_turns max_budget_usd max_thinking_tokens
|
|
52
52
|
strict_mcp_config mcp_servers hooks
|
|
53
|
-
settings sandbox cwd add_dirs env user
|
|
53
|
+
settings sandbox cwd add_dirs env user agent
|
|
54
54
|
cli_path extra_args agents setting_sources plugins
|
|
55
55
|
include_partial_messages output_format enable_file_checkpointing
|
|
56
56
|
persist_session betas max_buffer_size stderr_callback
|
|
@@ -60,7 +60,9 @@ module ClaudeAgent
|
|
|
60
60
|
attr_accessor(*ATTRIBUTES)
|
|
61
61
|
|
|
62
62
|
def initialize(**kwargs)
|
|
63
|
-
|
|
63
|
+
# Remove nil values so they don't override defaults
|
|
64
|
+
filtered = kwargs.compact
|
|
65
|
+
merged = DEFAULTS.merge(filtered)
|
|
64
66
|
ATTRIBUTES.each do |attr|
|
|
65
67
|
instance_variable_set(:"@#{attr}", merged[attr])
|
|
66
68
|
end
|
|
@@ -204,6 +206,7 @@ module ClaudeAgent
|
|
|
204
206
|
def environment_args
|
|
205
207
|
[].tap do |args|
|
|
206
208
|
args.push("--user", user) if user
|
|
209
|
+
args.push("--agent", agent) if agent
|
|
207
210
|
add_dirs.each { |dir| args.push("--add-dir", dir.to_s) }
|
|
208
211
|
args.push("--setting-sources", setting_sources.join(",")) if setting_sources&.any?
|
|
209
212
|
plugins.each do |plugin|
|
|
@@ -5,11 +5,12 @@ module ClaudeAgent
|
|
|
5
5
|
#
|
|
6
6
|
# @example Allow with modified input
|
|
7
7
|
# PermissionResultAllow.new(
|
|
8
|
-
# updated_input: input.merge("safe" => true)
|
|
8
|
+
# updated_input: input.merge("safe" => true),
|
|
9
|
+
# tool_use_id: "tool_123"
|
|
9
10
|
# )
|
|
10
11
|
#
|
|
11
|
-
PermissionResultAllow = Data.define(:updated_input, :updated_permissions) do
|
|
12
|
-
def initialize(updated_input: nil, updated_permissions: nil)
|
|
12
|
+
PermissionResultAllow = Data.define(:updated_input, :updated_permissions, :tool_use_id) do
|
|
13
|
+
def initialize(updated_input: nil, updated_permissions: nil, tool_use_id: nil)
|
|
13
14
|
super
|
|
14
15
|
end
|
|
15
16
|
|
|
@@ -21,6 +22,7 @@ module ClaudeAgent
|
|
|
21
22
|
h = { behavior: "allow" }
|
|
22
23
|
h[:updatedInput] = updated_input if updated_input
|
|
23
24
|
h[:updatedPermissions] = updated_permissions&.map { |p| p.respond_to?(:to_h) ? p.to_h : p } if updated_permissions
|
|
25
|
+
h[:toolUseID] = tool_use_id if tool_use_id
|
|
24
26
|
h
|
|
25
27
|
end
|
|
26
28
|
end
|
|
@@ -30,11 +32,12 @@ module ClaudeAgent
|
|
|
30
32
|
# @example Deny with message
|
|
31
33
|
# PermissionResultDeny.new(
|
|
32
34
|
# message: "Operation not allowed",
|
|
33
|
-
# interrupt: true
|
|
35
|
+
# interrupt: true,
|
|
36
|
+
# tool_use_id: "tool_123"
|
|
34
37
|
# )
|
|
35
38
|
#
|
|
36
|
-
PermissionResultDeny = Data.define(:message, :interrupt) do
|
|
37
|
-
def initialize(message: "", interrupt: false)
|
|
39
|
+
PermissionResultDeny = Data.define(:message, :interrupt, :tool_use_id) do
|
|
40
|
+
def initialize(message: "", interrupt: false, tool_use_id: nil)
|
|
38
41
|
super
|
|
39
42
|
end
|
|
40
43
|
|
|
@@ -43,7 +46,9 @@ module ClaudeAgent
|
|
|
43
46
|
end
|
|
44
47
|
|
|
45
48
|
def to_h
|
|
46
|
-
{ behavior: "deny", message: message, interrupt: interrupt }
|
|
49
|
+
h = { behavior: "deny", message: message, interrupt: interrupt }
|
|
50
|
+
h[:toolUseID] = tool_use_id if tool_use_id
|
|
51
|
+
h
|
|
47
52
|
end
|
|
48
53
|
end
|
|
49
54
|
|
|
@@ -141,7 +146,8 @@ module ClaudeAgent
|
|
|
141
146
|
# blocked_path: "/etc/passwd",
|
|
142
147
|
# decision_reason: "Path outside allowed directories",
|
|
143
148
|
# tool_use_id: "tool_123",
|
|
144
|
-
# agent_id: "agent_456"
|
|
149
|
+
# agent_id: "agent_456",
|
|
150
|
+
# signal: abort_signal
|
|
145
151
|
# )
|
|
146
152
|
#
|
|
147
153
|
ToolPermissionContext = Data.define(
|
|
@@ -149,14 +155,16 @@ module ClaudeAgent
|
|
|
149
155
|
:blocked_path,
|
|
150
156
|
:decision_reason,
|
|
151
157
|
:tool_use_id,
|
|
152
|
-
:agent_id
|
|
158
|
+
:agent_id,
|
|
159
|
+
:signal
|
|
153
160
|
) do
|
|
154
161
|
def initialize(
|
|
155
162
|
permission_suggestions: nil,
|
|
156
163
|
blocked_path: nil,
|
|
157
164
|
decision_reason: nil,
|
|
158
165
|
tool_use_id: nil,
|
|
159
|
-
agent_id: nil
|
|
166
|
+
agent_id: nil,
|
|
167
|
+
signal: nil
|
|
160
168
|
)
|
|
161
169
|
super
|
|
162
170
|
end
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ClaudeAgent
|
|
4
|
+
# V2 Session options (subset of full Options)
|
|
5
|
+
# V2 API - UNSTABLE
|
|
6
|
+
# @alpha
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# options = SessionOptions.new(
|
|
10
|
+
# model: "claude-sonnet-4-5-20250929",
|
|
11
|
+
# permission_mode: "acceptEdits"
|
|
12
|
+
# )
|
|
13
|
+
#
|
|
14
|
+
SessionOptions = Data.define(
|
|
15
|
+
:model,
|
|
16
|
+
:path_to_claude_code_executable,
|
|
17
|
+
:env,
|
|
18
|
+
:allowed_tools,
|
|
19
|
+
:disallowed_tools,
|
|
20
|
+
:can_use_tool,
|
|
21
|
+
:hooks,
|
|
22
|
+
:permission_mode
|
|
23
|
+
) do
|
|
24
|
+
def initialize(
|
|
25
|
+
model:,
|
|
26
|
+
path_to_claude_code_executable: nil,
|
|
27
|
+
env: nil,
|
|
28
|
+
allowed_tools: nil,
|
|
29
|
+
disallowed_tools: nil,
|
|
30
|
+
can_use_tool: nil,
|
|
31
|
+
hooks: nil,
|
|
32
|
+
permission_mode: nil
|
|
33
|
+
)
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# V2 API - UNSTABLE
|
|
39
|
+
# Multi-turn session interface for persistent conversations.
|
|
40
|
+
#
|
|
41
|
+
# This provides a simpler interface than the full Client class,
|
|
42
|
+
# matching the TypeScript SDK's SDKSession interface.
|
|
43
|
+
#
|
|
44
|
+
# @alpha
|
|
45
|
+
#
|
|
46
|
+
# @example Create a session and send messages
|
|
47
|
+
# session = ClaudeAgent.unstable_v2_create_session(model: "claude-sonnet-4-5-20250929")
|
|
48
|
+
# session.send("Hello!")
|
|
49
|
+
# session.stream.each { |msg| puts msg.inspect }
|
|
50
|
+
# session.close
|
|
51
|
+
#
|
|
52
|
+
class Session
|
|
53
|
+
attr_reader :session_id, :options
|
|
54
|
+
|
|
55
|
+
def initialize(options)
|
|
56
|
+
@options = options.is_a?(SessionOptions) ? options : SessionOptions.new(**options)
|
|
57
|
+
@client = nil
|
|
58
|
+
@session_id = nil
|
|
59
|
+
@closed = false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Send a message to the agent
|
|
63
|
+
#
|
|
64
|
+
# @param message [String, UserMessage] The message to send
|
|
65
|
+
# @return [void]
|
|
66
|
+
def send(message)
|
|
67
|
+
ensure_connected!
|
|
68
|
+
content = message.is_a?(String) ? message : message
|
|
69
|
+
@client.send_message(content)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Stream messages from the agent
|
|
73
|
+
#
|
|
74
|
+
# @return [Enumerator<message>] An enumerator of messages
|
|
75
|
+
# @yield [message] Each message received from the agent
|
|
76
|
+
def stream(&block)
|
|
77
|
+
ensure_connected!
|
|
78
|
+
if block_given?
|
|
79
|
+
@client.receive_response(&block)
|
|
80
|
+
else
|
|
81
|
+
@client.receive_response
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Close the session
|
|
86
|
+
#
|
|
87
|
+
# @return [void]
|
|
88
|
+
def close
|
|
89
|
+
return if @closed
|
|
90
|
+
@client&.disconnect
|
|
91
|
+
@closed = true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Check if session is closed
|
|
95
|
+
#
|
|
96
|
+
# @return [Boolean]
|
|
97
|
+
def closed?
|
|
98
|
+
@closed
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def ensure_connected!
|
|
104
|
+
raise AbortError, "Session is closed" if @closed
|
|
105
|
+
return if @client&.connected?
|
|
106
|
+
|
|
107
|
+
@client = Client.new(options: build_client_options)
|
|
108
|
+
@client.connect
|
|
109
|
+
update_session_id
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def build_client_options
|
|
113
|
+
Options.new(
|
|
114
|
+
model: @options.model,
|
|
115
|
+
cli_path: @options.path_to_claude_code_executable,
|
|
116
|
+
env: @options.env,
|
|
117
|
+
allowed_tools: @options.allowed_tools,
|
|
118
|
+
disallowed_tools: @options.disallowed_tools,
|
|
119
|
+
can_use_tool: @options.can_use_tool,
|
|
120
|
+
hooks: @options.hooks,
|
|
121
|
+
permission_mode: @options.permission_mode
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def update_session_id
|
|
126
|
+
# Session ID is typically extracted from the first system message
|
|
127
|
+
# but since we don't have it immediately, we leave it nil until available
|
|
128
|
+
@session_id = @client.server_info&.dig("session_id")
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class << self
|
|
133
|
+
# V2 API - UNSTABLE
|
|
134
|
+
# Create a persistent session for multi-turn conversations.
|
|
135
|
+
#
|
|
136
|
+
# @param options [Hash, SessionOptions] Session configuration
|
|
137
|
+
# @return [Session] A new session instance
|
|
138
|
+
# @alpha
|
|
139
|
+
#
|
|
140
|
+
# @example
|
|
141
|
+
# session = ClaudeAgent.unstable_v2_create_session(model: "claude-sonnet-4-5-20250929")
|
|
142
|
+
#
|
|
143
|
+
def unstable_v2_create_session(options)
|
|
144
|
+
Session.new(options)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# V2 API - UNSTABLE
|
|
148
|
+
# Resume an existing session by ID.
|
|
149
|
+
#
|
|
150
|
+
# @param session_id [String] The session ID to resume
|
|
151
|
+
# @param options [Hash, SessionOptions] Session configuration
|
|
152
|
+
# @return [Session] A session configured to resume the specified session
|
|
153
|
+
# @alpha
|
|
154
|
+
#
|
|
155
|
+
# @example
|
|
156
|
+
# session = ClaudeAgent.unstable_v2_resume_session("session-abc123", model: "claude-sonnet-4-5-20250929")
|
|
157
|
+
#
|
|
158
|
+
def unstable_v2_resume_session(session_id, options)
|
|
159
|
+
# For resumption, we need to pass the resume option through
|
|
160
|
+
# Since SessionOptions doesn't have resume, we handle it in the Client options
|
|
161
|
+
session = Session.new(options)
|
|
162
|
+
session.instance_variable_set(:@resume_session_id, session_id)
|
|
163
|
+
|
|
164
|
+
# Override build_client_options to include resume
|
|
165
|
+
session.define_singleton_method(:build_client_options) do
|
|
166
|
+
Options.new(
|
|
167
|
+
model: @options.model,
|
|
168
|
+
cli_path: @options.path_to_claude_code_executable,
|
|
169
|
+
env: @options.env,
|
|
170
|
+
allowed_tools: @options.allowed_tools,
|
|
171
|
+
disallowed_tools: @options.disallowed_tools,
|
|
172
|
+
can_use_tool: @options.can_use_tool,
|
|
173
|
+
hooks: @options.hooks,
|
|
174
|
+
permission_mode: @options.permission_mode,
|
|
175
|
+
resume: @resume_session_id
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
session
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# V2 API - UNSTABLE
|
|
183
|
+
# One-shot convenience function for single prompts.
|
|
184
|
+
#
|
|
185
|
+
# @param message [String] The prompt message
|
|
186
|
+
# @param options [Hash, SessionOptions] Session configuration
|
|
187
|
+
# @return [ResultMessage] The result of the query
|
|
188
|
+
# @alpha
|
|
189
|
+
#
|
|
190
|
+
# @example
|
|
191
|
+
# result = ClaudeAgent.unstable_v2_prompt("What files are here?", model: "claude-sonnet-4-5-20250929")
|
|
192
|
+
#
|
|
193
|
+
def unstable_v2_prompt(message, options)
|
|
194
|
+
session = unstable_v2_create_session(options)
|
|
195
|
+
begin
|
|
196
|
+
session.send(message)
|
|
197
|
+
result = nil
|
|
198
|
+
session.stream.each do |msg|
|
|
199
|
+
result = msg if msg.is_a?(ResultMessage)
|
|
200
|
+
end
|
|
201
|
+
result
|
|
202
|
+
ensure
|
|
203
|
+
session.close
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
data/lib/claude_agent/types.rb
CHANGED
|
@@ -83,7 +83,7 @@ module ClaudeAgent
|
|
|
83
83
|
# Per-model usage statistics returned in result messages (TypeScript SDK parity)
|
|
84
84
|
#
|
|
85
85
|
# @example
|
|
86
|
-
# usage = ModelUsage.new(input_tokens: 100, output_tokens: 50, cost_usd: 0.01)
|
|
86
|
+
# usage = ModelUsage.new(input_tokens: 100, output_tokens: 50, cost_usd: 0.01, max_output_tokens: 4096)
|
|
87
87
|
#
|
|
88
88
|
ModelUsage = Data.define(
|
|
89
89
|
:input_tokens,
|
|
@@ -92,7 +92,8 @@ module ClaudeAgent
|
|
|
92
92
|
:cache_creation_input_tokens,
|
|
93
93
|
:web_search_requests,
|
|
94
94
|
:cost_usd,
|
|
95
|
-
:context_window
|
|
95
|
+
:context_window,
|
|
96
|
+
:max_output_tokens
|
|
96
97
|
) do
|
|
97
98
|
def initialize(
|
|
98
99
|
input_tokens: 0,
|
|
@@ -101,7 +102,8 @@ module ClaudeAgent
|
|
|
101
102
|
cache_creation_input_tokens: 0,
|
|
102
103
|
web_search_requests: 0,
|
|
103
104
|
cost_usd: 0.0,
|
|
104
|
-
context_window: nil
|
|
105
|
+
context_window: nil,
|
|
106
|
+
max_output_tokens: nil
|
|
105
107
|
)
|
|
106
108
|
super
|
|
107
109
|
end
|
data/lib/claude_agent/version.rb
CHANGED
data/lib/claude_agent.rb
CHANGED
|
@@ -22,6 +22,7 @@ require_relative "claude_agent/mcp/tool"
|
|
|
22
22
|
require_relative "claude_agent/mcp/server"
|
|
23
23
|
require_relative "claude_agent/query"
|
|
24
24
|
require_relative "claude_agent/client"
|
|
25
|
+
require_relative "claude_agent/session" # V2 Session API (unstable)
|
|
25
26
|
|
|
26
27
|
module ClaudeAgent
|
|
27
28
|
# Re-export key classes at module level for convenience
|
data/sig/claude_agent.rbs
CHANGED
|
@@ -122,8 +122,9 @@ module ClaudeAgent
|
|
|
122
122
|
attr_reader web_search_requests: Integer
|
|
123
123
|
attr_reader cost_usd: Float
|
|
124
124
|
attr_reader context_window: Integer?
|
|
125
|
+
attr_reader max_output_tokens: Integer?
|
|
125
126
|
|
|
126
|
-
def initialize: (?input_tokens: Integer, ?output_tokens: Integer, ?cache_read_input_tokens: Integer, ?cache_creation_input_tokens: Integer, ?web_search_requests: Integer, ?cost_usd: Float, ?context_window: Integer?) -> void
|
|
127
|
+
def initialize: (?input_tokens: Integer, ?output_tokens: Integer, ?cache_read_input_tokens: Integer, ?cache_creation_input_tokens: Integer, ?web_search_requests: Integer, ?cost_usd: Float, ?context_window: Integer?, ?max_output_tokens: Integer?) -> void
|
|
127
128
|
end
|
|
128
129
|
|
|
129
130
|
class SDKPermissionDenial
|
|
@@ -278,6 +279,7 @@ module ClaudeAgent
|
|
|
278
279
|
attr_accessor add_dirs: Array[String]
|
|
279
280
|
attr_accessor env: Hash[String, String]
|
|
280
281
|
attr_accessor user: String?
|
|
282
|
+
attr_accessor agent: String?
|
|
281
283
|
|
|
282
284
|
# CLI configuration
|
|
283
285
|
attr_accessor cli_path: String?
|
|
@@ -632,8 +634,9 @@ module ClaudeAgent
|
|
|
632
634
|
class SessionStartInput < BaseHookInput
|
|
633
635
|
attr_reader source: String
|
|
634
636
|
attr_reader agent_type: String?
|
|
637
|
+
attr_reader model: String?
|
|
635
638
|
|
|
636
|
-
def initialize: (source: String, ?agent_type: String?, **untyped) -> void
|
|
639
|
+
def initialize: (source: String, ?agent_type: String?, ?model: String?, **untyped) -> void
|
|
637
640
|
end
|
|
638
641
|
|
|
639
642
|
class SessionEndInput < BaseHookInput
|
|
@@ -687,8 +690,9 @@ module ClaudeAgent
|
|
|
687
690
|
class PermissionResultAllow
|
|
688
691
|
attr_reader updated_input: Hash[String, untyped]?
|
|
689
692
|
attr_reader updated_permissions: Array[PermissionUpdate]?
|
|
693
|
+
attr_reader tool_use_id: String?
|
|
690
694
|
|
|
691
|
-
def initialize: (?updated_input: Hash[String, untyped]?, ?updated_permissions: Array[PermissionUpdate]?) -> void
|
|
695
|
+
def initialize: (?updated_input: Hash[String, untyped]?, ?updated_permissions: Array[PermissionUpdate]?, ?tool_use_id: String?) -> void
|
|
692
696
|
def behavior: () -> "allow"
|
|
693
697
|
def to_h: () -> Hash[Symbol, untyped]
|
|
694
698
|
end
|
|
@@ -696,8 +700,9 @@ module ClaudeAgent
|
|
|
696
700
|
class PermissionResultDeny
|
|
697
701
|
attr_reader message: String
|
|
698
702
|
attr_reader interrupt: bool
|
|
703
|
+
attr_reader tool_use_id: String?
|
|
699
704
|
|
|
700
|
-
def initialize: (?message: String, ?interrupt: bool) -> void
|
|
705
|
+
def initialize: (?message: String, ?interrupt: bool, ?tool_use_id: String?) -> void
|
|
701
706
|
def behavior: () -> "deny"
|
|
702
707
|
def to_h: () -> Hash[Symbol, untyped]
|
|
703
708
|
end
|
|
@@ -728,8 +733,9 @@ module ClaudeAgent
|
|
|
728
733
|
attr_reader decision_reason: String?
|
|
729
734
|
attr_reader tool_use_id: String?
|
|
730
735
|
attr_reader agent_id: String?
|
|
736
|
+
attr_reader signal: AbortSignal?
|
|
731
737
|
|
|
732
|
-
def initialize: (?permission_suggestions: untyped, ?blocked_path: String?, ?decision_reason: String?, ?tool_use_id: String?, ?agent_id: String?) -> void
|
|
738
|
+
def initialize: (?permission_suggestions: untyped, ?blocked_path: String?, ?decision_reason: String?, ?tool_use_id: String?, ?agent_id: String?, ?signal: AbortSignal?) -> void
|
|
733
739
|
end
|
|
734
740
|
|
|
735
741
|
# Client
|
|
@@ -909,4 +915,39 @@ module ClaudeAgent
|
|
|
909
915
|
def to_config: () -> Hash[Symbol, untyped]
|
|
910
916
|
end
|
|
911
917
|
end
|
|
918
|
+
|
|
919
|
+
# V2 Session API - UNSTABLE
|
|
920
|
+
# @alpha
|
|
921
|
+
|
|
922
|
+
# V2 Session options (subset of full Options)
|
|
923
|
+
class SessionOptions
|
|
924
|
+
attr_reader model: String
|
|
925
|
+
attr_reader path_to_claude_code_executable: String?
|
|
926
|
+
attr_reader env: Hash[String, String]?
|
|
927
|
+
attr_reader allowed_tools: Array[String]?
|
|
928
|
+
attr_reader disallowed_tools: Array[String]?
|
|
929
|
+
attr_reader can_use_tool: (^(String, Hash[String, untyped], untyped) -> permission_result)?
|
|
930
|
+
attr_reader hooks: Hash[String, Array[HookMatcher]]?
|
|
931
|
+
attr_reader permission_mode: String?
|
|
932
|
+
|
|
933
|
+
def initialize: (model: String, ?path_to_claude_code_executable: String?, ?env: Hash[String, String]?, ?allowed_tools: Array[String]?, ?disallowed_tools: Array[String]?, ?can_use_tool: (^(String, Hash[String, untyped], untyped) -> permission_result)?, ?hooks: Hash[String, Array[HookMatcher]]?, ?permission_mode: String?) -> void
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
# V2 Session interface for multi-turn conversations
|
|
937
|
+
class Session
|
|
938
|
+
attr_reader session_id: String?
|
|
939
|
+
attr_reader options: SessionOptions
|
|
940
|
+
|
|
941
|
+
def initialize: (Hash[Symbol, untyped] | SessionOptions options) -> void
|
|
942
|
+
def send: (String | UserMessage message) -> void
|
|
943
|
+
def stream: () -> Enumerator[message, void]
|
|
944
|
+
| () { (message) -> void } -> void
|
|
945
|
+
def close: () -> void
|
|
946
|
+
def closed?: () -> bool
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
# V2 API module-level methods
|
|
950
|
+
def self.unstable_v2_create_session: (Hash[Symbol, untyped] | SessionOptions options) -> Session
|
|
951
|
+
def self.unstable_v2_resume_session: (String session_id, Hash[Symbol, untyped] | SessionOptions options) -> Session
|
|
952
|
+
def self.unstable_v2_prompt: (String message, Hash[Symbol, untyped] | SessionOptions options) -> ResultMessage
|
|
912
953
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude_agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Thomas Carr
|
|
@@ -63,6 +63,7 @@ files:
|
|
|
63
63
|
- lib/claude_agent/permissions.rb
|
|
64
64
|
- lib/claude_agent/query.rb
|
|
65
65
|
- lib/claude_agent/sandbox_settings.rb
|
|
66
|
+
- lib/claude_agent/session.rb
|
|
66
67
|
- lib/claude_agent/spawn.rb
|
|
67
68
|
- lib/claude_agent/transport/base.rb
|
|
68
69
|
- lib/claude_agent/transport/subprocess.rb
|