claude-agent-sdk 0.28.0 → 0.29.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 +6 -0
- data/README.md +1 -1
- data/lib/claude_agent_sdk/command_builder.rb +79 -6
- data/lib/claude_agent_sdk/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2722d8b996148ef678484d587c1fcd88aaa68d9f3a380b342ad81b784f558cc
|
|
4
|
+
data.tar.gz: ce8a7d155a9c21fcddd4ad4dee0ec2b728d5397bb378d0b4c53f867febbc9cf0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a73b55a0228259e19e847683c4acccc6ac319f355f7d7fe9c8fd01dbc642e6ff7de767217b770dd95b8e3e847f280f46b49a7888f534278529b576412c29a7a
|
|
7
|
+
data.tar.gz: 5a1012c3ebd9a59f2a97476210b41873d616f053e7c02ba7c2bb385968f8134fb13b9f13730bb0c404b41877cc169dbde7c94f2d87b461671377492934511d0f
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.29.0] - 2026-08-09
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Skill names in `ClaudeAgentOptions#skills` are now validated** (port of Python SDK [#1145](https://github.com/anthropics/claude-agent-sdk-python/pull/1145), v0.2.129). Names were formatted into the `--allowedTools` value unchecked; the CLI splits that value into permission rules on commas and spaces outside parentheses with no escape sequences, so a name carrying a delimiter could not be passed through reliably. The command builder now validates each name and fails closed with `ArgumentError` (`TypeError` for non-String entries). Rejected: parentheses, commas, control characters (C0, DEL, C1), U+FEFF, empty names; a literal `*` and wildcard suffixes (`:*`, ` *`); and shapes that parse but can never match the listed skill — surrounding whitespace (Unicode-aware, unlike `String#strip`), a leading `/`, consecutive backslashes, a trailing unpaired backslash, and byte sequences that cannot form valid UTF-8 (Ruby's analogue of Python's surrogate check). Ordinary names are unaffected, including plugin-qualified names, interior spaces, single backslashes, and non-ASCII; valid non-UTF-8 strings are converted so the argv join cannot raise `Encoding::CompatibilityError`.
|
|
14
|
+
- **Breaking:** `skills: ['*']` and `skills: ['plugin:*']` now raise — use `skills: 'all'`, or a `Skill(...)` rule in `allowed_tools` for prefix matching. `skills: [' name']` and `skills: ['/name']` now raise as well; both previously built a rule that could never match, so the skill was silently unavailable.
|
|
15
|
+
|
|
10
16
|
## [0.28.0] - 2026-07-31
|
|
11
17
|
|
|
12
18
|
### Changed
|
data/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Add this line to your application's Gemfile:
|
|
|
68
68
|
gem 'claude-agent-sdk', github: 'ya-luotao/claude-agent-sdk-ruby'
|
|
69
69
|
|
|
70
70
|
# Or use a stable version from RubyGems
|
|
71
|
-
gem 'claude-agent-sdk', '~> 0.
|
|
71
|
+
gem 'claude-agent-sdk', '~> 0.29.0'
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
Then `bundle install`, or install directly: `gem install claude-agent-sdk`.
|
|
@@ -9,6 +9,16 @@ module ClaudeAgentSDK
|
|
|
9
9
|
class CommandBuilder
|
|
10
10
|
EXTRA_ARG_FLAG_REGEXP = /\A[a-z0-9][a-z0-9-]*\z/
|
|
11
11
|
|
|
12
|
+
# Parentheses and commas are delimiters to the --allowedTools tokenizer;
|
|
13
|
+
# control characters (C0, DEL, C1) never appear in a skill directory name.
|
|
14
|
+
# U+FEFF is here rather than in the whitespace checks below because the
|
|
15
|
+
# CLI trims it as whitespace and [[:space:]] does not match it.
|
|
16
|
+
SKILL_NAME_INVALID_CHARS = /[(),\u0000-\u001F\u007F-\u009F\uFEFF]/
|
|
17
|
+
|
|
18
|
+
# Unicode-aware edge whitespace: the CLI and Skill tool trim Unicode
|
|
19
|
+
# whitespace, and Ruby's String#strip is ASCII-only.
|
|
20
|
+
SKILL_NAME_EDGE_WHITESPACE = /\A[[:space:]]+|[[:space:]]+\z/
|
|
21
|
+
|
|
12
22
|
def initialize(cli_path, options)
|
|
13
23
|
@cli_path = cli_path
|
|
14
24
|
@options = options
|
|
@@ -93,26 +103,89 @@ module ClaudeAgentSDK
|
|
|
93
103
|
# entry, no duplicates) and default setting_sources to user+project so
|
|
94
104
|
# skill files are actually discovered. Explicit setting_sources (including
|
|
95
105
|
# []) is never overridden. Non-mutating; returns the effective pair.
|
|
96
|
-
#
|
|
97
|
-
#
|
|
106
|
+
# Each listed name is validated before being formatted into a rule (see
|
|
107
|
+
# #validate_skill_name). Both SDKs reject non-list, non-'all' shapes
|
|
108
|
+
# loudly; this raises ArgumentError where Python raises TypeError.
|
|
98
109
|
def skills_defaults
|
|
99
110
|
allowed_tools = @options.allowed_tools.dup
|
|
100
111
|
setting_sources = @options.setting_sources&.dup
|
|
101
112
|
skills = @options.skills
|
|
102
113
|
return [allowed_tools, setting_sources] if skills.nil?
|
|
103
114
|
|
|
104
|
-
# Fail loudly with a clear message instead of a bare NoMethodError from
|
|
105
|
-
# deep inside build for skills: :all / 'pdf' / Hash typos (and instead
|
|
106
|
-
# of Python's quirk of iterating a String's characters).
|
|
107
115
|
valid = skills == "all" || skills.is_a?(Array)
|
|
108
116
|
raise ArgumentError, "skills must be 'all' or an Array of skill names (got #{skills.inspect})" unless valid
|
|
109
117
|
|
|
110
|
-
entries = skills == "all" ? ["Skill"] : skills.map { |name| "Skill(#{name})" }
|
|
118
|
+
entries = skills == "all" ? ["Skill"] : skills.map { |name| "Skill(#{validate_skill_name(name)})" }
|
|
111
119
|
entries.each { |entry| allowed_tools << entry unless allowed_tools.include?(entry) }
|
|
112
120
|
setting_sources = %w[user project] if setting_sources.nil?
|
|
113
121
|
[allowed_tools, setting_sources]
|
|
114
122
|
end
|
|
115
123
|
|
|
124
|
+
# Reject skill names that cannot ride safely in a Skill(name) rule
|
|
125
|
+
# (port of Python SDK #1145). Names from options.skills are formatted
|
|
126
|
+
# into the --allowedTools value, which the CLI splits into rules on
|
|
127
|
+
# commas and spaces outside parentheses. That tokenizer honors no escape
|
|
128
|
+
# sequences -- escaping exists only in the per-rule grammar, applied
|
|
129
|
+
# after splitting -- so a name carrying a delimiter cannot be passed
|
|
130
|
+
# through reliably: what it tokenizes into depends on what surrounds it.
|
|
131
|
+
#
|
|
132
|
+
# Names that tokenize cleanly but can never match the listed skill are
|
|
133
|
+
# rejected too, so a dead rule fails loudly here instead of silently
|
|
134
|
+
# granting nothing. Returns the name as a UTF-8 String so later argv
|
|
135
|
+
# joins cannot raise Encoding::CompatibilityError.
|
|
136
|
+
def validate_skill_name(name) # rubocop:disable Metrics/MethodLength
|
|
137
|
+
raise TypeError, "Skill names must be strings, got #{name.class}: #{name.inspect}" unless name.is_a?(String)
|
|
138
|
+
|
|
139
|
+
# Ruby's analogue of Python's surrogate check: a lone surrogate (or any
|
|
140
|
+
# other broken byte sequence) is unrepresentable in valid UTF-8, and no
|
|
141
|
+
# CLI-discovered skill name contains one.
|
|
142
|
+
utf8 = begin
|
|
143
|
+
name.encode(Encoding::UTF_8)
|
|
144
|
+
rescue EncodingError
|
|
145
|
+
nil
|
|
146
|
+
end
|
|
147
|
+
if utf8.nil? || !utf8.valid_encoding?
|
|
148
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: contains bytes that cannot form " \
|
|
149
|
+
"valid UTF-8 (such as a surrogate code point), which can never match " \
|
|
150
|
+
"a skill the CLI discovered."
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
stripped = utf8.gsub(SKILL_NAME_EDGE_WHITESPACE, "")
|
|
154
|
+
raise ArgumentError, "Skill names must be non-empty strings" if stripped.empty?
|
|
155
|
+
|
|
156
|
+
if utf8 != stripped
|
|
157
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: leading or trailing whitespace " \
|
|
158
|
+
"can never match -- the Skill tool trims the invoked name."
|
|
159
|
+
end
|
|
160
|
+
if SKILL_NAME_INVALID_CHARS.match?(utf8)
|
|
161
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: parentheses, commas, control " \
|
|
162
|
+
"characters, and byte-order marks are not allowed. Names match the " \
|
|
163
|
+
"skill's directory name, or 'plugin:skill' for plugin-qualified skills."
|
|
164
|
+
end
|
|
165
|
+
raise ArgumentError, "Invalid skill name '*': use skills: 'all' to enable every skill." if utf8 == "*"
|
|
166
|
+
|
|
167
|
+
if utf8.end_with?(":*", " *")
|
|
168
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: wildcard-suffix names are not " \
|
|
169
|
+
"allowed; list each skill by its exact name."
|
|
170
|
+
end
|
|
171
|
+
if utf8.start_with?("/")
|
|
172
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: skill names may not start with " \
|
|
173
|
+
"'/'. The skills option takes the canonical name, not the " \
|
|
174
|
+
"slash-command form."
|
|
175
|
+
end
|
|
176
|
+
if utf8.include?("\\\\")
|
|
177
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: consecutive backslashes are not " \
|
|
178
|
+
"allowed -- the per-rule parser collapses them, so the rule would " \
|
|
179
|
+
"name a different skill."
|
|
180
|
+
end
|
|
181
|
+
if utf8.end_with?("\\")
|
|
182
|
+
raise ArgumentError, "Invalid skill name #{name.inspect}: names may not end with an " \
|
|
183
|
+
"unpaired backslash."
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
utf8
|
|
187
|
+
end
|
|
188
|
+
|
|
116
189
|
def append_disallowed_tools(cmd)
|
|
117
190
|
cmd.push("--disallowedTools", @options.disallowed_tools.join(",")) unless @options.disallowed_tools.empty?
|
|
118
191
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: claude-agent-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Community Contributors
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-08 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: async
|