claude-agent-sdk 1.1.0 → 1.2.1

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +10 -0
  3. data/CHANGELOG.md +99 -0
  4. data/README.md +43 -31
  5. data/docs/cli-installer.md +26 -4
  6. data/docs/client.md +29 -11
  7. data/docs/configuration.md +164 -1
  8. data/docs/errors.md +32 -2
  9. data/docs/hooks-and-permissions.md +30 -10
  10. data/docs/mcp-servers.md +30 -9
  11. data/docs/observability.md +61 -10
  12. data/docs/options.md +232 -0
  13. data/docs/rails.md +263 -18
  14. data/docs/sessions.md +40 -12
  15. data/docs/subagents.md +1 -1
  16. data/docs/types.md +100 -11
  17. data/lib/claude_agent_sdk/cli_installer.rb +140 -19
  18. data/lib/claude_agent_sdk/command_builder.rb +83 -108
  19. data/lib/claude_agent_sdk/fiber_boundary.rb +45 -2
  20. data/lib/claude_agent_sdk/instrumentation/otel.rb +90 -28
  21. data/lib/claude_agent_sdk/option_forms.rb +379 -0
  22. data/lib/claude_agent_sdk/query/run_lifecycle.rb +373 -0
  23. data/lib/claude_agent_sdk/query.rb +266 -364
  24. data/lib/claude_agent_sdk/railtie.rb +27 -2
  25. data/lib/claude_agent_sdk/sdk_mcp_server.rb +78 -26
  26. data/lib/claude_agent_sdk/session_assembly.rb +311 -0
  27. data/lib/claude_agent_sdk/session_mutations.rb +112 -92
  28. data/lib/claude_agent_sdk/session_resume.rb +360 -43
  29. data/lib/claude_agent_sdk/session_store.rb +31 -2
  30. data/lib/claude_agent_sdk/sessions.rb +720 -138
  31. data/lib/claude_agent_sdk/subprocess_cli_transport.rb +228 -29
  32. data/lib/claude_agent_sdk/testing/session_store_conformance.rb +18 -7
  33. data/lib/claude_agent_sdk/transcript_mirror_batcher.rb +45 -37
  34. data/lib/claude_agent_sdk/transport.rb +28 -12
  35. data/lib/claude_agent_sdk/types/attributes.rb +9 -0
  36. data/lib/claude_agent_sdk/types/base.rb +85 -15
  37. data/lib/claude_agent_sdk/types/hooks.rb +73 -0
  38. data/lib/claude_agent_sdk/types/mcp.rb +37 -1
  39. data/lib/claude_agent_sdk/types/option_values.rb +186 -4
  40. data/lib/claude_agent_sdk/types/options.rb +35 -5
  41. data/lib/claude_agent_sdk/types/permissions.rb +18 -9
  42. data/lib/claude_agent_sdk/version.rb +1 -1
  43. data/lib/claude_agent_sdk.rb +223 -418
  44. data/lib/generators/claude_agent_sdk/install/templates/claude_agent_sdk.rb.tt +6 -0
  45. data/sig/claude_agent_sdk/types/hooks.rbs +6 -3
  46. data/sig/claude_agent_sdk/types/option_values.rbs +23 -6
  47. data/sig/claude_agent_sdk/types/options.rbs +11 -7
  48. data/sig/claude_agent_sdk/types/permissions.rbs +4 -2
  49. metadata +9 -4
@@ -18,8 +18,10 @@ module ClaudeAgentSDK
18
18
 
19
19
  # Claude Agent Options for configuring queries
20
20
  class ClaudeAgentOptions < Type
21
- # `env` routinely carries credentials (ANTHROPIC_API_KEY, ...).
22
- inspect_filtered :env
21
+ # `env` routinely carries credentials (ANTHROPIC_API_KEY, ...). So does
22
+ # `settings` (its own `env`, `apiKeyHelper`), as a Hash or a JSON String,
23
+ # and an `extra_args` value can be one.
24
+ inspect_filtered :env, :settings, :extra_args
23
25
 
24
26
  attr_accessor :allowed_tools, :system_prompt, :mcp_servers, :permission_mode,
25
27
  :resume, :resume_session_at, :session_id, :max_turns, :disallowed_tools,
@@ -345,16 +347,44 @@ module ClaudeAgentSDK
345
347
  self.callback_scheduling = :thread if callback_scheduling.nil?
346
348
  end
347
349
 
350
+ # `mcp_servers` holds typed configs, which filter themselves, next to raw
351
+ # Hash configs carrying the same credentials: those are rendered by
352
+ # Type#inspect_mcp_server_config. Anything but a Hash (the JSON of a
353
+ # config as a String, or the path to one) is replaced outright, like a
354
+ # String `settings`.
355
+ def inspect_attributes
356
+ super.map { |name, value| [name, name == 'mcp_servers' ? inspect_mcp_servers(value) : value] }
357
+ end
358
+
359
+ def inspect_mcp_servers(servers)
360
+ return '[FILTERED]' unless servers.is_a?(Hash)
361
+
362
+ servers.to_h { |name, config| [name, config.is_a?(Hash) ? inspect_mcp_server_config(config) : config] }
363
+ rescue StandardError
364
+ '[FILTERED]'
365
+ end
366
+
348
367
  # Strict key validation: unlike other Type subclasses (which silently drop
349
368
  # unknown keys for forward-compat with newer CLI output), ClaudeAgentOptions
350
369
  # is a developer-facing config object — typos should fail loudly.
351
370
  def assign_attribute(name, value)
352
- setter = :"#{normalize_name(name)}="
353
- raise ArgumentError, "unknown ClaudeAgentOptions option: #{name.inspect}" unless respond_to?(setter)
371
+ setter = option_setter(normalize_name(name))
372
+ raise ArgumentError, "unknown ClaudeAgentOptions option: #{name.inspect}" unless setter
354
373
 
355
374
  public_send(setter, value)
356
375
  end
357
376
 
377
+ # The writer of the option with this normalized name, nil when the name
378
+ # is not an option. An option is a declared attribute, or a setter user
379
+ # code defined (on a subclass, a module it includes, or the object);
380
+ # respond_to? alone would also answer for '[]' (#[]=) and '=' (#==).
381
+ def option_setter(normalized)
382
+ setter = :"#{normalized}="
383
+ return unless respond_to?(setter)
384
+
385
+ setter if self.class.attribute?(normalized) || user_defined_method?(setter)
386
+ end
387
+
358
388
  # Merge caller-provided attributes with configured defaults.
359
389
  # Only keys the caller explicitly passed are treated as overrides;
360
390
  # method-signature defaults ([], {}, false) are NOT present unless the caller wrote them.
@@ -403,7 +433,7 @@ module ClaudeAgentSDK
403
433
  # reports the typo exactly as the developer wrote it.
404
434
  def option_key(name)
405
435
  normalized = normalize_name(name)
406
- respond_to?(:"#{normalized}=") ? normalized.to_sym : name
436
+ option_setter(normalized) ? normalized.to_sym : name
407
437
  end
408
438
  end
409
439
  end
@@ -34,20 +34,21 @@ module ClaudeAgentSDK
34
34
  @rules = value&.map { |rule| rule.is_a?(Hash) ? PermissionRuleValue.new(rule) : rule }
35
35
  end
36
36
 
37
+ # The wire form. The CLI validates the updatedPermissions of a
38
+ # can_use_tool reply as one unit and drops the whole array when a single
39
+ # entry does not fit its schema, so two things it rejects are never
40
+ # written: a rule without content has no ruleContent key (the schema
41
+ # wants a String or no key, not null), and an update that has a type but
42
+ # no destination goes to 'session' — the narrowest one: it lasts for this
43
+ # run and writes no settings file (the default Python PR #1330 proposes).
37
44
  def to_h
38
45
  result = { type: @type }
39
- result[:destination] = @destination if @destination
46
+ destination = @destination || ('session' unless @type.nil?)
47
+ result[:destination] = destination if destination
40
48
 
41
49
  case @type
42
50
  when 'addRules', 'replaceRules', 'removeRules'
43
- if @rules
44
- result[:rules] = @rules.map do |rule|
45
- {
46
- toolName: rule.tool_name,
47
- ruleContent: rule.rule_content
48
- }
49
- end
50
- end
51
+ result[:rules] = @rules.map { |rule| rule_to_h(rule) } if @rules
51
52
  result[:behavior] = @behavior if @behavior
52
53
  when 'setMode'
53
54
  result[:mode] = @mode if @mode
@@ -57,6 +58,14 @@ module ClaudeAgentSDK
57
58
 
58
59
  result
59
60
  end
61
+
62
+ private
63
+
64
+ def rule_to_h(rule)
65
+ wire_rule = { toolName: rule.tool_name }
66
+ wire_rule[:ruleContent] = rule.rule_content unless rule.rule_content.nil?
67
+ wire_rule
68
+ end
60
69
  end
61
70
 
62
71
  # Tool permission context delivered to `can_use_tool` callbacks.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClaudeAgentSDK
4
- VERSION = '1.1.0'
4
+ VERSION = '1.2.1'
5
5
  end