claude_agent 0.7.12 → 0.7.14
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/.claude/rules/testing.md +51 -10
- data/.claude/settings.json +1 -0
- data/ARCHITECTURE.md +237 -0
- data/CHANGELOG.md +53 -0
- data/CLAUDE.md +2 -0
- data/README.md +46 -1
- data/Rakefile +17 -0
- data/SPEC.md +214 -125
- data/lib/claude_agent/client/commands.rb +225 -0
- data/lib/claude_agent/client.rb +4 -206
- data/lib/claude_agent/content_blocks/generic_block.rb +39 -0
- data/lib/claude_agent/content_blocks/image_content_block.rb +54 -0
- data/lib/claude_agent/content_blocks/server_tool_result_block.rb +22 -0
- data/lib/claude_agent/content_blocks/server_tool_use_block.rb +48 -0
- data/lib/claude_agent/content_blocks/text_block.rb +19 -0
- data/lib/claude_agent/content_blocks/thinking_block.rb +19 -0
- data/lib/claude_agent/content_blocks/tool_result_block.rb +25 -0
- data/lib/claude_agent/content_blocks/tool_use_block.rb +134 -0
- data/lib/claude_agent/content_blocks.rb +8 -335
- data/lib/claude_agent/control_protocol/commands.rb +304 -0
- data/lib/claude_agent/control_protocol/lifecycle.rb +116 -0
- data/lib/claude_agent/control_protocol/messaging.rb +163 -0
- data/lib/claude_agent/control_protocol/primitives.rb +168 -0
- data/lib/claude_agent/control_protocol/request_handling.rb +231 -0
- data/lib/claude_agent/control_protocol.rb +50 -882
- data/lib/claude_agent/conversation.rb +8 -1
- data/lib/claude_agent/event_handler.rb +1 -0
- data/lib/claude_agent/get_session_info.rb +86 -0
- data/lib/claude_agent/hooks.rb +23 -2
- data/lib/claude_agent/list_sessions.rb +22 -13
- data/lib/claude_agent/message_parser.rb +26 -4
- data/lib/claude_agent/messages/conversation.rb +138 -0
- data/lib/claude_agent/messages/generic.rb +39 -0
- data/lib/claude_agent/messages/hook_lifecycle.rb +158 -0
- data/lib/claude_agent/messages/result.rb +80 -0
- data/lib/claude_agent/messages/streaming.rb +84 -0
- data/lib/claude_agent/messages/system.rb +67 -0
- data/lib/claude_agent/messages/task_lifecycle.rb +240 -0
- data/lib/claude_agent/messages/tool_lifecycle.rb +95 -0
- data/lib/claude_agent/messages.rb +11 -829
- data/lib/claude_agent/options/serializer.rb +194 -0
- data/lib/claude_agent/options.rb +11 -176
- data/lib/claude_agent/query.rb +0 -2
- data/lib/claude_agent/sandbox_settings.rb +3 -0
- data/lib/claude_agent/session.rb +0 -204
- data/lib/claude_agent/session_mutations.rb +148 -0
- data/lib/claude_agent/transport/subprocess.rb +2 -2
- data/lib/claude_agent/types/mcp.rb +30 -0
- data/lib/claude_agent/types/models.rb +146 -0
- data/lib/claude_agent/types/operations.rb +38 -0
- data/lib/claude_agent/types/sessions.rb +50 -0
- data/lib/claude_agent/types/tools.rb +32 -0
- data/lib/claude_agent/types.rb +6 -264
- data/lib/claude_agent/v2_session.rb +207 -0
- data/lib/claude_agent/version.rb +1 -1
- data/lib/claude_agent.rb +37 -3
- data/sig/claude_agent.rbs +144 -13
- metadata +33 -1
data/lib/claude_agent/types.rb
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "types/tools"
|
|
4
|
+
require_relative "types/models"
|
|
5
|
+
require_relative "types/mcp"
|
|
6
|
+
require_relative "types/sessions"
|
|
7
|
+
require_relative "types/operations"
|
|
8
|
+
|
|
3
9
|
module ClaudeAgent
|
|
4
10
|
# Assistant message error types (TypeScript SDK parity)
|
|
5
11
|
# Used to categorize errors returned by the assistant
|
|
@@ -16,268 +22,4 @@ module ClaudeAgent
|
|
|
16
22
|
# API key source types (TypeScript SDK parity)
|
|
17
23
|
# Indicates where the API key was sourced from
|
|
18
24
|
API_KEY_SOURCES = %w[user project org temporary oauth].freeze
|
|
19
|
-
|
|
20
|
-
# Tools preset configuration (TypeScript SDK parity)
|
|
21
|
-
#
|
|
22
|
-
# @example
|
|
23
|
-
# preset = ToolsPreset.new(preset: "claude_code")
|
|
24
|
-
# options = ClaudeAgent::Options.new(tools: preset)
|
|
25
|
-
#
|
|
26
|
-
ToolsPreset = Data.define(:type, :preset) do
|
|
27
|
-
def initialize(type: "preset", preset: "claude_code")
|
|
28
|
-
super
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def to_h
|
|
32
|
-
{ type: type, preset: preset }
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
# Return type for supported_commands() (TypeScript SDK parity)
|
|
36
|
-
#
|
|
37
|
-
# @example
|
|
38
|
-
# cmd = SlashCommand.new(name: "commit", description: "Create a commit", argument_hint: "[message]")
|
|
39
|
-
# cmd.name # => "commit"
|
|
40
|
-
# cmd.description # => "Create a commit"
|
|
41
|
-
#
|
|
42
|
-
SlashCommand = Data.define(:name, :description, :argument_hint) do
|
|
43
|
-
def initialize(name:, description: nil, argument_hint: nil)
|
|
44
|
-
super
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Return type for supported_models() (TypeScript SDK parity)
|
|
49
|
-
#
|
|
50
|
-
# @example
|
|
51
|
-
# model = ModelInfo.new(value: "claude-3-opus", display_name: "Claude 3 Opus", description: "Most capable")
|
|
52
|
-
# model.value # => "claude-3-opus"
|
|
53
|
-
# model.display_name # => "Claude 3 Opus"
|
|
54
|
-
#
|
|
55
|
-
ModelInfo = Data.define(:value, :display_name, :description, :supports_effort, :supported_effort_levels, :supports_adaptive_thinking) do
|
|
56
|
-
def initialize(value:, display_name: nil, description: nil, supports_effort: nil, supported_effort_levels: nil, supports_adaptive_thinking: nil)
|
|
57
|
-
super
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Return type for mcp_server_status() (TypeScript SDK parity)
|
|
62
|
-
# Status values: "connected", "failed", "needs-auth", "pending"
|
|
63
|
-
#
|
|
64
|
-
# @example
|
|
65
|
-
# status = McpServerStatus.new(name: "filesystem", status: "connected", server_info: {name: "fs", version: "1.0"})
|
|
66
|
-
#
|
|
67
|
-
McpServerStatus = Data.define(:name, :status, :server_info, :error, :config, :scope, :tools) do
|
|
68
|
-
def initialize(name:, status:, server_info: nil, error: nil, config: nil, scope: nil, tools: nil)
|
|
69
|
-
super
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Task usage statistics for TaskNotificationMessage (TypeScript SDK parity)
|
|
74
|
-
#
|
|
75
|
-
# @example
|
|
76
|
-
# usage = TaskUsage.new(total_tokens: 5000, tool_uses: 3, duration_ms: 2500)
|
|
77
|
-
#
|
|
78
|
-
TaskUsage = Data.define(:total_tokens, :tool_uses, :duration_ms) do
|
|
79
|
-
def initialize(total_tokens: 0, tool_uses: 0, duration_ms: 0)
|
|
80
|
-
super
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Return type for account_info() (TypeScript SDK parity)
|
|
85
|
-
#
|
|
86
|
-
# @example
|
|
87
|
-
# info = AccountInfo.new(email: "user@example.com", organization: "Acme Corp")
|
|
88
|
-
#
|
|
89
|
-
AccountInfo = Data.define(:email, :organization, :subscription_type, :token_source, :api_key_source) do
|
|
90
|
-
def initialize(email: nil, organization: nil, subscription_type: nil, token_source: nil, api_key_source: nil)
|
|
91
|
-
super
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Composite initialization result from supported_commands request (TypeScript SDK parity)
|
|
96
|
-
#
|
|
97
|
-
# @example
|
|
98
|
-
# result = InitializationResult.new(
|
|
99
|
-
# commands: [SlashCommand.new(name: "commit")],
|
|
100
|
-
# output_style: "default",
|
|
101
|
-
# available_output_styles: ["default", "concise"],
|
|
102
|
-
# models: [ModelInfo.new(value: "claude-sonnet")],
|
|
103
|
-
# account: AccountInfo.new(email: "user@example.com")
|
|
104
|
-
# )
|
|
105
|
-
#
|
|
106
|
-
InitializationResult = Data.define(:commands, :output_style, :available_output_styles, :models, :account) do
|
|
107
|
-
def initialize(commands: [], output_style: nil, available_output_styles: [], models: [], account: nil)
|
|
108
|
-
super
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Per-model usage statistics returned in result messages (TypeScript SDK parity)
|
|
113
|
-
#
|
|
114
|
-
# @example
|
|
115
|
-
# usage = ModelUsage.new(input_tokens: 100, output_tokens: 50, cost_usd: 0.01, max_output_tokens: 4096)
|
|
116
|
-
#
|
|
117
|
-
ModelUsage = Data.define(
|
|
118
|
-
:input_tokens,
|
|
119
|
-
:output_tokens,
|
|
120
|
-
:cache_read_input_tokens,
|
|
121
|
-
:cache_creation_input_tokens,
|
|
122
|
-
:web_search_requests,
|
|
123
|
-
:cost_usd,
|
|
124
|
-
:context_window,
|
|
125
|
-
:max_output_tokens
|
|
126
|
-
) do
|
|
127
|
-
def initialize(
|
|
128
|
-
input_tokens: 0,
|
|
129
|
-
output_tokens: 0,
|
|
130
|
-
cache_read_input_tokens: 0,
|
|
131
|
-
cache_creation_input_tokens: 0,
|
|
132
|
-
web_search_requests: 0,
|
|
133
|
-
cost_usd: 0.0,
|
|
134
|
-
context_window: nil,
|
|
135
|
-
max_output_tokens: nil
|
|
136
|
-
)
|
|
137
|
-
super
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Permission denial information in result messages (TypeScript SDK parity)
|
|
142
|
-
#
|
|
143
|
-
SDKPermissionDenial = Data.define(:tool_name, :tool_use_id, :tool_input) do
|
|
144
|
-
def initialize(tool_name:, tool_use_id:, tool_input:)
|
|
145
|
-
super
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Result of set_mcp_servers() control method (TypeScript SDK parity)
|
|
150
|
-
#
|
|
151
|
-
# @example
|
|
152
|
-
# result = McpSetServersResult.new(
|
|
153
|
-
# added: ["server1"],
|
|
154
|
-
# removed: ["old-server"],
|
|
155
|
-
# errors: {"server2" => "Connection failed"}
|
|
156
|
-
# )
|
|
157
|
-
#
|
|
158
|
-
McpSetServersResult = Data.define(:added, :removed, :errors) do
|
|
159
|
-
def initialize(added: [], removed: [], errors: {})
|
|
160
|
-
super
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Result of rewind_files() control method (TypeScript SDK parity)
|
|
165
|
-
#
|
|
166
|
-
# @example
|
|
167
|
-
# result = RewindFilesResult.new(
|
|
168
|
-
# can_rewind: true,
|
|
169
|
-
# files_changed: ["src/foo.rb", "src/bar.rb"],
|
|
170
|
-
# insertions: 10,
|
|
171
|
-
# deletions: 5
|
|
172
|
-
# )
|
|
173
|
-
#
|
|
174
|
-
RewindFilesResult = Data.define(:can_rewind, :error, :files_changed, :insertions, :deletions) do
|
|
175
|
-
def initialize(can_rewind:, error: nil, files_changed: nil, insertions: nil, deletions: nil)
|
|
176
|
-
super
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Agent definition for custom subagents (TypeScript SDK parity)
|
|
181
|
-
#
|
|
182
|
-
# @example Basic agent
|
|
183
|
-
# agent = AgentDefinition.new(
|
|
184
|
-
# description: "Runs tests and reports results",
|
|
185
|
-
# prompt: "You are a test runner...",
|
|
186
|
-
# tools: ["Read", "Grep", "Glob", "Bash"],
|
|
187
|
-
# model: "haiku"
|
|
188
|
-
# )
|
|
189
|
-
#
|
|
190
|
-
# @example Agent with skills and max_turns
|
|
191
|
-
# agent = AgentDefinition.new(
|
|
192
|
-
# description: "Research agent with specialized skills",
|
|
193
|
-
# prompt: "You are a research expert...",
|
|
194
|
-
# skills: ["web-search", "summarization"],
|
|
195
|
-
# max_turns: 10
|
|
196
|
-
# )
|
|
197
|
-
#
|
|
198
|
-
# Session metadata returned by list_sessions (TypeScript SDK parity: SDKSessionInfo)
|
|
199
|
-
#
|
|
200
|
-
# @example
|
|
201
|
-
# session = SessionInfo.new(
|
|
202
|
-
# session_id: "abc-123",
|
|
203
|
-
# summary: "Fix login bug",
|
|
204
|
-
# last_modified: 1706000000000,
|
|
205
|
-
# file_size: 4096,
|
|
206
|
-
# custom_title: "Login fix",
|
|
207
|
-
# first_prompt: "Help me fix the login page",
|
|
208
|
-
# git_branch: "fix/login",
|
|
209
|
-
# cwd: "/Users/dev/myapp"
|
|
210
|
-
# )
|
|
211
|
-
#
|
|
212
|
-
SessionInfo = Data.define(
|
|
213
|
-
:session_id,
|
|
214
|
-
:summary,
|
|
215
|
-
:last_modified,
|
|
216
|
-
:file_size,
|
|
217
|
-
:custom_title,
|
|
218
|
-
:first_prompt,
|
|
219
|
-
:git_branch,
|
|
220
|
-
:cwd
|
|
221
|
-
) do
|
|
222
|
-
def initialize(session_id:, summary:, last_modified:, file_size:, custom_title: nil, first_prompt: nil, git_branch: nil, cwd: nil)
|
|
223
|
-
super
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# Message from a session transcript returned by get_session_messages (TypeScript SDK v0.2.59 parity)
|
|
228
|
-
#
|
|
229
|
-
# @example
|
|
230
|
-
# msg = SessionMessage.new(
|
|
231
|
-
# type: "user",
|
|
232
|
-
# uuid: "abc-123",
|
|
233
|
-
# session_id: "def-456",
|
|
234
|
-
# message: { "role" => "user", "content" => [{ "type" => "text", "text" => "Hello" }] }
|
|
235
|
-
# )
|
|
236
|
-
#
|
|
237
|
-
SessionMessage = Data.define(:type, :uuid, :session_id, :message, :parent_tool_use_id) do
|
|
238
|
-
def initialize(type:, uuid:, session_id:, message:, parent_tool_use_id: nil)
|
|
239
|
-
super
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
AgentDefinition = Data.define(
|
|
244
|
-
:description,
|
|
245
|
-
:prompt,
|
|
246
|
-
:tools,
|
|
247
|
-
:disallowed_tools,
|
|
248
|
-
:model,
|
|
249
|
-
:mcp_servers,
|
|
250
|
-
:critical_system_reminder,
|
|
251
|
-
:skills,
|
|
252
|
-
:max_turns
|
|
253
|
-
) do
|
|
254
|
-
def initialize(
|
|
255
|
-
description:,
|
|
256
|
-
prompt:,
|
|
257
|
-
tools: nil,
|
|
258
|
-
disallowed_tools: nil,
|
|
259
|
-
model: nil,
|
|
260
|
-
mcp_servers: nil,
|
|
261
|
-
critical_system_reminder: nil,
|
|
262
|
-
skills: nil,
|
|
263
|
-
max_turns: nil
|
|
264
|
-
)
|
|
265
|
-
super
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
def to_h
|
|
269
|
-
result = {
|
|
270
|
-
description: description,
|
|
271
|
-
prompt: prompt
|
|
272
|
-
}
|
|
273
|
-
result[:tools] = tools if tools
|
|
274
|
-
result[:disallowedTools] = disallowed_tools if disallowed_tools
|
|
275
|
-
result[:model] = model if model
|
|
276
|
-
result[:mcpServers] = mcp_servers if mcp_servers
|
|
277
|
-
result[:criticalSystemReminder_EXPERIMENTAL] = critical_system_reminder if critical_system_reminder
|
|
278
|
-
result[:skills] = skills if skills
|
|
279
|
-
result[:maxTurns] = max_turns if max_turns
|
|
280
|
-
result
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
25
|
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 V2Session
|
|
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
|
+
V2Session.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 = V2Session.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/version.rb
CHANGED
data/lib/claude_agent.rb
CHANGED
|
@@ -36,7 +36,10 @@ require_relative "claude_agent/session_paths" # Shared session path infra
|
|
|
36
36
|
require_relative "claude_agent/list_sessions" # Session discovery (TypeScript SDK v0.2.53 parity)
|
|
37
37
|
require_relative "claude_agent/get_session_messages" # Session transcript reading (TypeScript SDK v0.2.59 parity)
|
|
38
38
|
require_relative "claude_agent/session_message_relation" # Chainable message query object
|
|
39
|
-
require_relative "claude_agent/
|
|
39
|
+
require_relative "claude_agent/session_mutations" # Session rename/tag mutations
|
|
40
|
+
require_relative "claude_agent/get_session_info" # Single session lookup
|
|
41
|
+
require_relative "claude_agent/v2_session" # V2 Session API (unstable)
|
|
42
|
+
require_relative "claude_agent/session" # Session finder
|
|
40
43
|
|
|
41
44
|
module ClaudeAgent
|
|
42
45
|
class << self
|
|
@@ -56,9 +59,11 @@ module ClaudeAgent
|
|
|
56
59
|
# @param dir [String, nil] Directory to scope sessions to (includes git worktrees).
|
|
57
60
|
# When nil, returns sessions from all projects.
|
|
58
61
|
# @param limit [Integer, nil] Maximum number of sessions to return.
|
|
62
|
+
# @param include_worktrees [Boolean] When dir is in a git repo, include sessions
|
|
63
|
+
# from all git worktree paths. Defaults to true.
|
|
59
64
|
# @return [Array<SessionInfo>]
|
|
60
|
-
def list_sessions(dir: nil, limit: nil)
|
|
61
|
-
ListSessions.call(dir: dir, limit: limit)
|
|
65
|
+
def list_sessions(dir: nil, limit: nil, offset: nil, include_worktrees: true)
|
|
66
|
+
ListSessions.call(dir: dir, limit: limit, offset: offset, include_worktrees: include_worktrees)
|
|
62
67
|
end
|
|
63
68
|
|
|
64
69
|
# Read messages from a past session's transcript
|
|
@@ -76,6 +81,35 @@ module ClaudeAgent
|
|
|
76
81
|
GetSessionMessages.call(session_id, dir: dir, limit: limit, offset: offset)
|
|
77
82
|
end
|
|
78
83
|
|
|
84
|
+
# Rename a session by appending a custom-title entry to its file.
|
|
85
|
+
#
|
|
86
|
+
# @param session_id [String] UUID of the session to rename
|
|
87
|
+
# @param title [String] New title
|
|
88
|
+
# @param dir [String, nil] Project directory to scope the search
|
|
89
|
+
# @return [void]
|
|
90
|
+
def rename_session(session_id, title, dir: nil)
|
|
91
|
+
SessionMutations.rename_session(session_id, title, dir: dir)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Tag a session by appending a tag entry to its file.
|
|
95
|
+
#
|
|
96
|
+
# @param session_id [String] UUID of the session to tag
|
|
97
|
+
# @param tag [String, nil] Tag value. Pass nil to clear.
|
|
98
|
+
# @param dir [String, nil] Project directory to scope the search
|
|
99
|
+
# @return [void]
|
|
100
|
+
def tag_session(session_id, tag, dir: nil)
|
|
101
|
+
SessionMutations.tag_session(session_id, tag, dir: dir)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Look up a single session by ID.
|
|
105
|
+
#
|
|
106
|
+
# @param session_id [String] UUID of the session
|
|
107
|
+
# @param dir [String, nil] Project directory to scope the search
|
|
108
|
+
# @return [SessionInfo, nil]
|
|
109
|
+
def get_session_info(session_id, dir: nil)
|
|
110
|
+
GetSessionInfo.call(session_id, dir: dir)
|
|
111
|
+
end
|
|
112
|
+
|
|
79
113
|
# Resume a previous Conversation by session ID
|
|
80
114
|
#
|
|
81
115
|
# @param session_id [String] Session ID to resume
|