claude-agent-sdk 0.16.1 → 0.16.2
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 +5 -0
- data/README.md +1 -1
- data/lib/claude_agent_sdk/command_builder.rb +291 -0
- data/lib/claude_agent_sdk/subprocess_cli_transport.rb +2 -239
- data/lib/claude_agent_sdk/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 184cf490ead1c830e0733d720e8f3580310ea3d68880e4e2945b28f803bea83b
|
|
4
|
+
data.tar.gz: fd3d83127d247eec43f30852ee8e5a75b14420721ad32b14e2ee7a811ed1d673
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 054c00ec53645630c0a60e66d78503b46da03f6bc3b43f9021c4dd3274d3309345af83751a7556074e7303205f1d502361c9f1d339a8f9201aea92b5f0dfda6a
|
|
7
|
+
data.tar.gz: e32f7b8a2ed41fe1c5db98e5254b1abddcfffe4f935508fe61eeef1c10ed11c1bb14a344fea16f46300d1fd3e0c8e150884b0340600dd161ec1c9582a0ff4289
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.16.2] - 2026-04-23
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Extracted `ClaudeAgentSDK::CommandBuilder` from `SubprocessCLITransport`. CLI argv assembly now lives in its own class and can be exercised in isolation — `CommandBuilder.new(cli_path, options).build` returns the argv array without booting a transport. No public behavior change; `SubprocessCLITransport#build_command` still works and now delegates to `CommandBuilder`.
|
|
14
|
+
|
|
10
15
|
## [0.16.1] - 2026-04-21
|
|
11
16
|
|
|
12
17
|
### Fixed
|
data/README.md
CHANGED
|
@@ -108,7 +108,7 @@ Add this line to your application's Gemfile:
|
|
|
108
108
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
109
109
|
|
|
110
110
|
# Or use a stable version from RubyGems
|
|
111
|
-
gem 'claude-agent-sdk', '~> 0.16.
|
|
111
|
+
gem 'claude-agent-sdk', '~> 0.16.2'
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
And then execute:
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
require_relative "types"
|
|
6
|
+
|
|
7
|
+
module ClaudeAgentSDK
|
|
8
|
+
# Builds the CLI argv array from a ClaudeAgentOptions instance.
|
|
9
|
+
class CommandBuilder
|
|
10
|
+
EXTRA_ARG_FLAG_REGEXP = /\A[a-z0-9][a-z0-9-]*\z/
|
|
11
|
+
|
|
12
|
+
def initialize(cli_path, options)
|
|
13
|
+
@cli_path = cli_path
|
|
14
|
+
@options = options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def build
|
|
18
|
+
cmd = [@cli_path, "--output-format", "stream-json", "--verbose"]
|
|
19
|
+
|
|
20
|
+
append_system_prompt(cmd)
|
|
21
|
+
append_tools(cmd)
|
|
22
|
+
append_allowed_tools(cmd)
|
|
23
|
+
append_disallowed_tools(cmd)
|
|
24
|
+
append_max_turns(cmd)
|
|
25
|
+
append_model(cmd)
|
|
26
|
+
append_permission(cmd)
|
|
27
|
+
append_session(cmd)
|
|
28
|
+
append_settings(cmd)
|
|
29
|
+
append_budget(cmd)
|
|
30
|
+
append_thinking(cmd)
|
|
31
|
+
append_effort(cmd)
|
|
32
|
+
append_betas(cmd)
|
|
33
|
+
append_append_allowed_tools(cmd)
|
|
34
|
+
append_output_format(cmd)
|
|
35
|
+
append_additional_dirs(cmd)
|
|
36
|
+
append_mcp_servers(cmd)
|
|
37
|
+
append_boolean_flags(cmd)
|
|
38
|
+
append_plugins(cmd)
|
|
39
|
+
append_setting_sources(cmd)
|
|
40
|
+
append_extra_args(cmd)
|
|
41
|
+
|
|
42
|
+
# Always use streaming mode for bidirectional control protocol.
|
|
43
|
+
# Prompts and agents are sent via stdin (initialize + user messages),
|
|
44
|
+
# which avoids OS ARG_MAX limits for large prompts and agent configurations.
|
|
45
|
+
cmd.push("--input-format", "stream-json")
|
|
46
|
+
|
|
47
|
+
cmd
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def append_system_prompt(cmd)
|
|
53
|
+
case @options.system_prompt
|
|
54
|
+
when nil
|
|
55
|
+
# When nil, pass empty string to ensure predictable behavior without default Claude Code system prompt
|
|
56
|
+
cmd.push("--system-prompt", "")
|
|
57
|
+
when String
|
|
58
|
+
cmd.push("--system-prompt", @options.system_prompt)
|
|
59
|
+
when SystemPromptFile
|
|
60
|
+
cmd.push("--system-prompt-file", @options.system_prompt.path)
|
|
61
|
+
when SystemPromptPreset
|
|
62
|
+
# Preset activates the default Claude Code system prompt by not passing --system-prompt ""
|
|
63
|
+
# Only --append-system-prompt is passed if append text is provided
|
|
64
|
+
cmd.push("--append-system-prompt", @options.system_prompt.append) if @options.system_prompt.append
|
|
65
|
+
when Hash
|
|
66
|
+
append_hash_system_prompt(cmd, @options.system_prompt)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def append_hash_system_prompt(cmd, prompt_hash)
|
|
71
|
+
prompt_type = prompt_hash[:type] || prompt_hash["type"]
|
|
72
|
+
case prompt_type
|
|
73
|
+
when "file"
|
|
74
|
+
prompt_path = prompt_hash[:path] || prompt_hash["path"]
|
|
75
|
+
cmd.push("--system-prompt-file", prompt_path) if prompt_path
|
|
76
|
+
when "preset"
|
|
77
|
+
append = prompt_hash[:append] || prompt_hash["append"]
|
|
78
|
+
# Preset activates the default Claude Code system prompt by not passing --system-prompt ""
|
|
79
|
+
cmd.push("--append-system-prompt", append) if append
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def append_allowed_tools(cmd)
|
|
84
|
+
cmd.push("--allowedTools", @options.allowed_tools.join(",")) unless @options.allowed_tools.empty?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def append_disallowed_tools(cmd)
|
|
88
|
+
cmd.push("--disallowedTools", @options.disallowed_tools.join(",")) unless @options.disallowed_tools.empty?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def append_max_turns(cmd)
|
|
92
|
+
cmd.push("--max-turns", @options.max_turns.to_s) if @options.max_turns
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def append_model(cmd)
|
|
96
|
+
cmd.push("--model", @options.model) if @options.model
|
|
97
|
+
cmd.push("--fallback-model", @options.fallback_model) if @options.fallback_model
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def append_permission(cmd)
|
|
101
|
+
cmd.push("--permission-prompt-tool", @options.permission_prompt_tool_name) if @options.permission_prompt_tool_name
|
|
102
|
+
cmd.push("--permission-mode", @options.permission_mode) if @options.permission_mode
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def append_session(cmd)
|
|
106
|
+
cmd.push("--continue") if @options.continue_conversation
|
|
107
|
+
cmd.push("--resume", @options.resume) if @options.resume
|
|
108
|
+
cmd.push("--session-id", @options.session_id) if @options.session_id
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def append_settings(cmd)
|
|
112
|
+
return unless @options.settings || @options.sandbox
|
|
113
|
+
|
|
114
|
+
settings_hash = {}
|
|
115
|
+
settings_is_path = false
|
|
116
|
+
|
|
117
|
+
if @options.settings
|
|
118
|
+
if @options.settings.is_a?(String)
|
|
119
|
+
begin
|
|
120
|
+
settings_hash = JSON.parse(@options.settings)
|
|
121
|
+
rescue JSON::ParserError
|
|
122
|
+
if @options.sandbox
|
|
123
|
+
settings_hash = load_settings_file(@options.settings)
|
|
124
|
+
else
|
|
125
|
+
settings_is_path = true
|
|
126
|
+
cmd.push("--settings", @options.settings)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
elsif @options.settings.is_a?(Hash)
|
|
130
|
+
settings_hash = @options.settings.dup
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if !settings_is_path && @options.sandbox
|
|
135
|
+
sandbox_hash = @options.sandbox.is_a?(SandboxSettings) ? @options.sandbox.to_h : @options.sandbox
|
|
136
|
+
settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
cmd.push("--settings", JSON.generate(settings_hash)) if !settings_is_path && !settings_hash.empty?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def append_budget(cmd)
|
|
143
|
+
cmd.push("--max-budget-usd", @options.max_budget_usd.to_s) if @options.max_budget_usd
|
|
144
|
+
|
|
145
|
+
return unless @options.task_budget
|
|
146
|
+
|
|
147
|
+
total = if @options.task_budget.is_a?(TaskBudget)
|
|
148
|
+
@options.task_budget.total
|
|
149
|
+
else
|
|
150
|
+
@options.task_budget[:total] || @options.task_budget["total"]
|
|
151
|
+
end
|
|
152
|
+
cmd.push("--task-budget", total.to_s) if total
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Thinking configuration takes precedence over deprecated max_thinking_tokens
|
|
156
|
+
def append_thinking(cmd)
|
|
157
|
+
if @options.thinking
|
|
158
|
+
case @options.thinking
|
|
159
|
+
when ThinkingConfigAdaptive
|
|
160
|
+
cmd.push("--thinking", "adaptive")
|
|
161
|
+
when ThinkingConfigEnabled
|
|
162
|
+
cmd.push("--max-thinking-tokens", @options.thinking.budget_tokens.to_s)
|
|
163
|
+
when ThinkingConfigDisabled
|
|
164
|
+
cmd.push("--thinking", "disabled")
|
|
165
|
+
end
|
|
166
|
+
elsif @options.max_thinking_tokens
|
|
167
|
+
cmd.push("--max-thinking-tokens", @options.max_thinking_tokens.to_s)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# The set of supported levels is model-dependent; the CLI falls back to
|
|
172
|
+
# the highest supported level at or below the one requested
|
|
173
|
+
# (e.g. `xhigh` → `high` on Opus 4.6).
|
|
174
|
+
def append_effort(cmd)
|
|
175
|
+
cmd.push("--effort", @options.effort.to_s) if @options.effort
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def append_betas(cmd)
|
|
179
|
+
return unless @options.betas && !@options.betas.empty?
|
|
180
|
+
|
|
181
|
+
cmd.push("--betas", @options.betas.join(","))
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def append_tools(cmd)
|
|
185
|
+
return if @options.tools.nil?
|
|
186
|
+
|
|
187
|
+
case @options.tools
|
|
188
|
+
when Array
|
|
189
|
+
tools_value = @options.tools.empty? ? "" : @options.tools.join(",")
|
|
190
|
+
cmd.push("--tools", tools_value)
|
|
191
|
+
when ToolsPreset
|
|
192
|
+
cmd.push("--tools", "default")
|
|
193
|
+
when Hash
|
|
194
|
+
if (@options.tools[:type] || @options.tools["type"]) == "preset"
|
|
195
|
+
cmd.push("--tools", "default")
|
|
196
|
+
else
|
|
197
|
+
cmd.push("--tools", JSON.generate(@options.tools))
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def append_append_allowed_tools(cmd)
|
|
203
|
+
return unless @options.append_allowed_tools && !@options.append_allowed_tools.empty?
|
|
204
|
+
|
|
205
|
+
cmd.push("--append-allowed-tools", @options.append_allowed_tools.join(","))
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def append_output_format(cmd)
|
|
209
|
+
return unless @options.output_format
|
|
210
|
+
|
|
211
|
+
schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == "json_schema"
|
|
212
|
+
@options.output_format[:schema]
|
|
213
|
+
elsif @options.output_format.is_a?(Hash) && @options.output_format["type"] == "json_schema"
|
|
214
|
+
@options.output_format["schema"]
|
|
215
|
+
else
|
|
216
|
+
@options.output_format
|
|
217
|
+
end
|
|
218
|
+
schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
|
|
219
|
+
cmd.push("--json-schema", schema_json)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def append_additional_dirs(cmd)
|
|
223
|
+
@options.add_dirs.each { |dir| cmd.push("--add-dir", dir.to_s) }
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def append_mcp_servers(cmd)
|
|
227
|
+
return unless @options.mcp_servers && !@options.mcp_servers.empty?
|
|
228
|
+
|
|
229
|
+
if @options.mcp_servers.is_a?(Hash)
|
|
230
|
+
servers_for_cli = {}
|
|
231
|
+
@options.mcp_servers.each do |name, config|
|
|
232
|
+
servers_for_cli[name] = if config.is_a?(Hash) && config[:type] == "sdk"
|
|
233
|
+
config.except(:instance)
|
|
234
|
+
else
|
|
235
|
+
config
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
cmd.push("--mcp-config", JSON.generate({ mcpServers: servers_for_cli })) unless servers_for_cli.empty?
|
|
239
|
+
else
|
|
240
|
+
cmd.push("--mcp-config", @options.mcp_servers.to_s)
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# NOTE: agents are sent via the initialize control request (not CLI args)
|
|
245
|
+
# to avoid OS ARG_MAX limits with large agent configurations.
|
|
246
|
+
def append_boolean_flags(cmd)
|
|
247
|
+
cmd.push("--include-partial-messages") if @options.include_partial_messages
|
|
248
|
+
cmd.push("--fork-session") if @options.fork_session
|
|
249
|
+
cmd.push("--bare") if @options.bare
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def append_plugins(cmd)
|
|
253
|
+
return unless @options.plugins && !@options.plugins.empty?
|
|
254
|
+
|
|
255
|
+
@options.plugins.each do |plugin|
|
|
256
|
+
plugin_config = plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
|
|
257
|
+
plugin_type = plugin_config[:type] || plugin_config["type"]
|
|
258
|
+
plugin_path = plugin_config[:path] || plugin_config["path"]
|
|
259
|
+
|
|
260
|
+
raise ArgumentError, "Unsupported plugin type: #{plugin_type.inspect}" unless %w[local plugin].include?(plugin_type)
|
|
261
|
+
next unless plugin_path
|
|
262
|
+
|
|
263
|
+
cmd.push("--plugin-dir", plugin_path)
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def append_setting_sources(cmd)
|
|
268
|
+
return unless @options.setting_sources
|
|
269
|
+
|
|
270
|
+
cmd.push("--setting-sources", @options.setting_sources.join(","))
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def append_extra_args(cmd)
|
|
274
|
+
@options.extra_args.each do |flag, value|
|
|
275
|
+
raise ArgumentError, "Invalid extra_args flag name: #{flag.inspect} (expected lowercase kebab-case)" unless EXTRA_ARG_FLAG_REGEXP.match?(flag)
|
|
276
|
+
|
|
277
|
+
if value.nil?
|
|
278
|
+
cmd.push("--#{flag}")
|
|
279
|
+
else
|
|
280
|
+
cmd.push("--#{flag}", value.to_s)
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def load_settings_file(path)
|
|
286
|
+
raise CLIConnectionError, "Settings file not found: #{path}" unless File.file?(path)
|
|
287
|
+
|
|
288
|
+
JSON.parse(File.read(path))
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
@@ -6,13 +6,13 @@ require 'timeout'
|
|
|
6
6
|
require_relative 'transport'
|
|
7
7
|
require_relative 'errors'
|
|
8
8
|
require_relative 'version'
|
|
9
|
+
require_relative 'command_builder'
|
|
9
10
|
|
|
10
11
|
module ClaudeAgentSDK
|
|
11
12
|
# Subprocess transport using Claude Code CLI
|
|
12
13
|
class SubprocessCLITransport < Transport
|
|
13
14
|
DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 # 1MB buffer limit
|
|
14
15
|
MINIMUM_CLAUDE_CODE_VERSION = '2.0.0'
|
|
15
|
-
EXTRA_ARG_FLAG_RE = /\A[a-z0-9][a-z0-9-]*\z/
|
|
16
16
|
|
|
17
17
|
def initialize(options_or_prompt = nil, options = nil)
|
|
18
18
|
# Support both new single-arg form and legacy two-arg form
|
|
@@ -67,126 +67,7 @@ module ClaudeAgentSDK
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def build_command
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# System prompt handling
|
|
73
|
-
# When nil, pass empty string to ensure predictable behavior without default Claude Code system prompt
|
|
74
|
-
if @options.system_prompt.nil?
|
|
75
|
-
cmd.concat(['--system-prompt', ''])
|
|
76
|
-
elsif @options.system_prompt.is_a?(String)
|
|
77
|
-
cmd.concat(['--system-prompt', @options.system_prompt])
|
|
78
|
-
elsif @options.system_prompt.is_a?(SystemPromptFile)
|
|
79
|
-
cmd.concat(['--system-prompt-file', @options.system_prompt.path])
|
|
80
|
-
elsif @options.system_prompt.is_a?(SystemPromptPreset)
|
|
81
|
-
# Preset activates the default Claude Code system prompt by not passing --system-prompt ""
|
|
82
|
-
# Only --append-system-prompt is passed if append text is provided
|
|
83
|
-
cmd.concat(['--append-system-prompt', @options.system_prompt.append]) if @options.system_prompt.append
|
|
84
|
-
elsif @options.system_prompt.is_a?(Hash)
|
|
85
|
-
prompt_type = @options.system_prompt[:type] || @options.system_prompt['type']
|
|
86
|
-
if prompt_type == 'file'
|
|
87
|
-
prompt_path = @options.system_prompt[:path] || @options.system_prompt['path']
|
|
88
|
-
cmd.concat(['--system-prompt-file', prompt_path]) if prompt_path
|
|
89
|
-
elsif prompt_type == 'preset'
|
|
90
|
-
append = @options.system_prompt[:append] || @options.system_prompt['append']
|
|
91
|
-
# Preset activates the default Claude Code system prompt by not passing --system-prompt ""
|
|
92
|
-
cmd.concat(['--append-system-prompt', append]) if append
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
cmd.concat(['--allowedTools', @options.allowed_tools.join(',')]) unless @options.allowed_tools.empty?
|
|
97
|
-
cmd.concat(['--max-turns', @options.max_turns.to_s]) if @options.max_turns
|
|
98
|
-
cmd.concat(['--disallowedTools', @options.disallowed_tools.join(',')]) unless @options.disallowed_tools.empty?
|
|
99
|
-
cmd.concat(['--model', @options.model]) if @options.model
|
|
100
|
-
cmd.concat(['--fallback-model', @options.fallback_model]) if @options.fallback_model
|
|
101
|
-
cmd.concat(['--permission-prompt-tool', @options.permission_prompt_tool_name]) if @options.permission_prompt_tool_name
|
|
102
|
-
cmd.concat(['--permission-mode', @options.permission_mode]) if @options.permission_mode
|
|
103
|
-
cmd << '--continue' if @options.continue_conversation
|
|
104
|
-
cmd.concat(['--resume', @options.resume]) if @options.resume
|
|
105
|
-
cmd.concat(['--session-id', @options.session_id]) if @options.session_id
|
|
106
|
-
|
|
107
|
-
# Settings handling with sandbox merge
|
|
108
|
-
build_settings_args(cmd)
|
|
109
|
-
|
|
110
|
-
# Budget limit option
|
|
111
|
-
cmd.concat(['--max-budget-usd', @options.max_budget_usd.to_s]) if @options.max_budget_usd
|
|
112
|
-
|
|
113
|
-
# Task budget (API-side token budget)
|
|
114
|
-
if @options.task_budget
|
|
115
|
-
total = if @options.task_budget.is_a?(TaskBudget)
|
|
116
|
-
@options.task_budget.total
|
|
117
|
-
else
|
|
118
|
-
@options.task_budget[:total] || @options.task_budget['total']
|
|
119
|
-
end
|
|
120
|
-
cmd.concat(['--task-budget', total.to_s]) if total
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# Thinking configuration (takes precedence over deprecated max_thinking_tokens)
|
|
124
|
-
build_thinking_args(cmd)
|
|
125
|
-
|
|
126
|
-
# Effort level — see ClaudeAgentSDK::EFFORT_LEVELS. The set of supported
|
|
127
|
-
# levels is model-dependent; the CLI falls back to the highest supported
|
|
128
|
-
# level at or below the one requested (e.g. `xhigh` → `high` on Opus 4.6).
|
|
129
|
-
cmd.concat(['--effort', @options.effort.to_s]) if @options.effort
|
|
130
|
-
|
|
131
|
-
# Betas option for enabling experimental features
|
|
132
|
-
if @options.betas && !@options.betas.empty?
|
|
133
|
-
cmd.concat(['--betas', @options.betas.join(',')])
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Tools option for base tools selection
|
|
137
|
-
build_tools_args(cmd)
|
|
138
|
-
|
|
139
|
-
# Append allowed tools option
|
|
140
|
-
if @options.append_allowed_tools && !@options.append_allowed_tools.empty?
|
|
141
|
-
cmd.concat(['--append-allowed-tools', @options.append_allowed_tools.join(',')])
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# JSON schema for structured output
|
|
145
|
-
build_output_format_args(cmd)
|
|
146
|
-
|
|
147
|
-
# Add directories
|
|
148
|
-
@options.add_dirs.each do |dir|
|
|
149
|
-
cmd.concat(['--add-dir', dir.to_s])
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# MCP servers
|
|
153
|
-
build_mcp_servers_args(cmd)
|
|
154
|
-
|
|
155
|
-
cmd << '--include-partial-messages' if @options.include_partial_messages
|
|
156
|
-
cmd << '--fork-session' if @options.fork_session
|
|
157
|
-
cmd << '--bare' if @options.bare
|
|
158
|
-
|
|
159
|
-
# Note: agents are now sent via the initialize control request (not CLI args)
|
|
160
|
-
# to avoid OS ARG_MAX limits with large agent configurations.
|
|
161
|
-
|
|
162
|
-
# Plugins
|
|
163
|
-
build_plugins_args(cmd)
|
|
164
|
-
|
|
165
|
-
# Setting sources
|
|
166
|
-
if @options.setting_sources
|
|
167
|
-
cmd.concat(['--setting-sources', @options.setting_sources.join(',')])
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Extra args
|
|
171
|
-
@options.extra_args.each do |flag, value|
|
|
172
|
-
flag_str = flag.to_s
|
|
173
|
-
unless EXTRA_ARG_FLAG_RE.match?(flag_str)
|
|
174
|
-
raise ArgumentError, "Invalid extra_args flag name: #{flag.inspect} (expected lowercase kebab-case)"
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
if value.nil?
|
|
178
|
-
cmd << "--#{flag_str}"
|
|
179
|
-
else
|
|
180
|
-
cmd.concat(["--#{flag_str}", value.to_s])
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# Always use streaming mode for bidirectional control protocol.
|
|
185
|
-
# Prompts and agents are sent via stdin (initialize + user messages),
|
|
186
|
-
# which avoids OS ARG_MAX limits for large prompts and agent configurations.
|
|
187
|
-
cmd.concat(['--input-format', 'stream-json'])
|
|
188
|
-
|
|
189
|
-
cmd
|
|
70
|
+
CommandBuilder.new(@cli_path, @options).build
|
|
190
71
|
end
|
|
191
72
|
|
|
192
73
|
def connect
|
|
@@ -514,123 +395,5 @@ module ClaudeAgentSDK
|
|
|
514
395
|
def ready?
|
|
515
396
|
@ready
|
|
516
397
|
end
|
|
517
|
-
|
|
518
|
-
private
|
|
519
|
-
|
|
520
|
-
def build_settings_args(cmd)
|
|
521
|
-
return unless @options.settings || @options.sandbox
|
|
522
|
-
|
|
523
|
-
settings_hash = {}
|
|
524
|
-
settings_is_path = false
|
|
525
|
-
|
|
526
|
-
if @options.settings
|
|
527
|
-
if @options.settings.is_a?(String)
|
|
528
|
-
begin
|
|
529
|
-
settings_hash = JSON.parse(@options.settings)
|
|
530
|
-
rescue JSON::ParserError
|
|
531
|
-
if @options.sandbox
|
|
532
|
-
settings_hash = load_settings_file(@options.settings)
|
|
533
|
-
else
|
|
534
|
-
settings_is_path = true
|
|
535
|
-
cmd.concat(['--settings', @options.settings])
|
|
536
|
-
end
|
|
537
|
-
end
|
|
538
|
-
elsif @options.settings.is_a?(Hash)
|
|
539
|
-
settings_hash = @options.settings.dup
|
|
540
|
-
end
|
|
541
|
-
end
|
|
542
|
-
|
|
543
|
-
if !settings_is_path && @options.sandbox
|
|
544
|
-
sandbox_hash = @options.sandbox.is_a?(SandboxSettings) ? @options.sandbox.to_h : @options.sandbox
|
|
545
|
-
settings_hash[:sandbox] = sandbox_hash unless sandbox_hash.empty?
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
cmd.concat(['--settings', JSON.generate(settings_hash)]) if !settings_is_path && !settings_hash.empty?
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
def build_tools_args(cmd)
|
|
552
|
-
return if @options.tools.nil?
|
|
553
|
-
|
|
554
|
-
if @options.tools.is_a?(Array)
|
|
555
|
-
tools_value = @options.tools.empty? ? '' : @options.tools.join(',')
|
|
556
|
-
cmd.concat(['--tools', tools_value])
|
|
557
|
-
elsif @options.tools.is_a?(ToolsPreset)
|
|
558
|
-
cmd.concat(['--tools', 'default'])
|
|
559
|
-
elsif @options.tools.is_a?(Hash)
|
|
560
|
-
if (@options.tools[:type] || @options.tools['type']) == 'preset'
|
|
561
|
-
cmd.concat(['--tools', 'default'])
|
|
562
|
-
else
|
|
563
|
-
cmd.concat(['--tools', JSON.generate(@options.tools)])
|
|
564
|
-
end
|
|
565
|
-
end
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
def build_output_format_args(cmd)
|
|
569
|
-
return unless @options.output_format
|
|
570
|
-
|
|
571
|
-
schema = if @options.output_format.is_a?(Hash) && @options.output_format[:type] == 'json_schema'
|
|
572
|
-
@options.output_format[:schema]
|
|
573
|
-
elsif @options.output_format.is_a?(Hash) && @options.output_format['type'] == 'json_schema'
|
|
574
|
-
@options.output_format['schema']
|
|
575
|
-
else
|
|
576
|
-
@options.output_format
|
|
577
|
-
end
|
|
578
|
-
schema_json = schema.is_a?(String) ? schema : JSON.generate(schema)
|
|
579
|
-
cmd.concat(['--json-schema', schema_json])
|
|
580
|
-
end
|
|
581
|
-
|
|
582
|
-
def build_mcp_servers_args(cmd)
|
|
583
|
-
return unless @options.mcp_servers && !@options.mcp_servers.empty?
|
|
584
|
-
|
|
585
|
-
if @options.mcp_servers.is_a?(Hash)
|
|
586
|
-
servers_for_cli = {}
|
|
587
|
-
@options.mcp_servers.each do |name, config|
|
|
588
|
-
servers_for_cli[name] = if config.is_a?(Hash) && config[:type] == 'sdk'
|
|
589
|
-
config.reject { |k, _| k == :instance }
|
|
590
|
-
else
|
|
591
|
-
config
|
|
592
|
-
end
|
|
593
|
-
end
|
|
594
|
-
cmd.concat(['--mcp-config', JSON.generate({ mcpServers: servers_for_cli })]) unless servers_for_cli.empty?
|
|
595
|
-
else
|
|
596
|
-
cmd.concat(['--mcp-config', @options.mcp_servers.to_s])
|
|
597
|
-
end
|
|
598
|
-
end
|
|
599
|
-
|
|
600
|
-
def build_plugins_args(cmd)
|
|
601
|
-
return unless @options.plugins && !@options.plugins.empty?
|
|
602
|
-
|
|
603
|
-
@options.plugins.each do |plugin|
|
|
604
|
-
plugin_config = plugin.is_a?(SdkPluginConfig) ? plugin.to_h : plugin
|
|
605
|
-
plugin_type = plugin_config[:type] || plugin_config['type']
|
|
606
|
-
plugin_path = plugin_config[:path] || plugin_config['path']
|
|
607
|
-
|
|
608
|
-
raise ArgumentError, "Unsupported plugin type: #{plugin_type.inspect}" unless %w[local plugin].include?(plugin_type)
|
|
609
|
-
next unless plugin_path
|
|
610
|
-
|
|
611
|
-
cmd.concat(['--plugin-dir', plugin_path])
|
|
612
|
-
end
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
def load_settings_file(path)
|
|
616
|
-
raise CLIConnectionError, "Settings file not found: #{path}" unless File.file?(path)
|
|
617
|
-
|
|
618
|
-
JSON.parse(File.read(path))
|
|
619
|
-
end
|
|
620
|
-
|
|
621
|
-
def build_thinking_args(cmd)
|
|
622
|
-
if @options.thinking
|
|
623
|
-
case @options.thinking
|
|
624
|
-
when ThinkingConfigAdaptive
|
|
625
|
-
cmd.concat(['--thinking', 'adaptive'])
|
|
626
|
-
when ThinkingConfigEnabled
|
|
627
|
-
cmd.concat(['--max-thinking-tokens', @options.thinking.budget_tokens.to_s])
|
|
628
|
-
when ThinkingConfigDisabled
|
|
629
|
-
cmd.concat(['--thinking', 'disabled'])
|
|
630
|
-
end
|
|
631
|
-
elsif @options.max_thinking_tokens
|
|
632
|
-
cmd.concat(['--max-thinking-tokens', @options.max_thinking_tokens.to_s])
|
|
633
|
-
end
|
|
634
|
-
end
|
|
635
398
|
end
|
|
636
399
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.16.
|
|
4
|
+
version: 0.16.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-04-
|
|
10
|
+
date: 2026-04-23 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: async
|
|
@@ -104,6 +104,7 @@ files:
|
|
|
104
104
|
- LICENSE
|
|
105
105
|
- README.md
|
|
106
106
|
- lib/claude_agent_sdk.rb
|
|
107
|
+
- lib/claude_agent_sdk/command_builder.rb
|
|
107
108
|
- lib/claude_agent_sdk/configuration.rb
|
|
108
109
|
- lib/claude_agent_sdk/errors.rb
|
|
109
110
|
- lib/claude_agent_sdk/fiber_boundary.rb
|