a2a-ruby 1.0.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.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +137 -0
  4. data/.simplecov +46 -0
  5. data/.yardopts +10 -0
  6. data/CHANGELOG.md +33 -0
  7. data/CODE_OF_CONDUCT.md +128 -0
  8. data/CONTRIBUTING.md +165 -0
  9. data/Gemfile +43 -0
  10. data/Guardfile +34 -0
  11. data/LICENSE.txt +21 -0
  12. data/PUBLISHING_CHECKLIST.md +214 -0
  13. data/README.md +171 -0
  14. data/Rakefile +165 -0
  15. data/docs/agent_execution.md +309 -0
  16. data/docs/api_reference.md +792 -0
  17. data/docs/configuration.md +780 -0
  18. data/docs/events.md +475 -0
  19. data/docs/getting_started.md +668 -0
  20. data/docs/integration.md +262 -0
  21. data/docs/server_apps.md +621 -0
  22. data/docs/troubleshooting.md +765 -0
  23. data/lib/a2a/client/api_methods.rb +263 -0
  24. data/lib/a2a/client/auth/api_key.rb +161 -0
  25. data/lib/a2a/client/auth/interceptor.rb +288 -0
  26. data/lib/a2a/client/auth/jwt.rb +189 -0
  27. data/lib/a2a/client/auth/oauth2.rb +146 -0
  28. data/lib/a2a/client/auth.rb +137 -0
  29. data/lib/a2a/client/base.rb +316 -0
  30. data/lib/a2a/client/config.rb +210 -0
  31. data/lib/a2a/client/connection_pool.rb +233 -0
  32. data/lib/a2a/client/http_client.rb +524 -0
  33. data/lib/a2a/client/json_rpc_handler.rb +136 -0
  34. data/lib/a2a/client/middleware/circuit_breaker_interceptor.rb +245 -0
  35. data/lib/a2a/client/middleware/logging_interceptor.rb +371 -0
  36. data/lib/a2a/client/middleware/rate_limit_interceptor.rb +142 -0
  37. data/lib/a2a/client/middleware/retry_interceptor.rb +161 -0
  38. data/lib/a2a/client/middleware.rb +116 -0
  39. data/lib/a2a/client/performance_tracker.rb +60 -0
  40. data/lib/a2a/configuration/defaults.rb +34 -0
  41. data/lib/a2a/configuration/environment_loader.rb +76 -0
  42. data/lib/a2a/configuration/file_loader.rb +115 -0
  43. data/lib/a2a/configuration/inheritance.rb +101 -0
  44. data/lib/a2a/configuration/validator.rb +180 -0
  45. data/lib/a2a/configuration.rb +201 -0
  46. data/lib/a2a/errors.rb +291 -0
  47. data/lib/a2a/modules.rb +50 -0
  48. data/lib/a2a/monitoring/alerting.rb +490 -0
  49. data/lib/a2a/monitoring/distributed_tracing.rb +398 -0
  50. data/lib/a2a/monitoring/health_endpoints.rb +204 -0
  51. data/lib/a2a/monitoring/metrics_collector.rb +438 -0
  52. data/lib/a2a/monitoring.rb +463 -0
  53. data/lib/a2a/plugin.rb +358 -0
  54. data/lib/a2a/plugin_manager.rb +159 -0
  55. data/lib/a2a/plugins/example_auth.rb +81 -0
  56. data/lib/a2a/plugins/example_middleware.rb +118 -0
  57. data/lib/a2a/plugins/example_transport.rb +76 -0
  58. data/lib/a2a/protocol/agent_card.rb +8 -0
  59. data/lib/a2a/protocol/agent_card_server.rb +584 -0
  60. data/lib/a2a/protocol/capability.rb +496 -0
  61. data/lib/a2a/protocol/json_rpc.rb +254 -0
  62. data/lib/a2a/protocol/message.rb +8 -0
  63. data/lib/a2a/protocol/task.rb +8 -0
  64. data/lib/a2a/rails/a2a_controller.rb +258 -0
  65. data/lib/a2a/rails/controller_helpers.rb +499 -0
  66. data/lib/a2a/rails/engine.rb +167 -0
  67. data/lib/a2a/rails/generators/agent_generator.rb +311 -0
  68. data/lib/a2a/rails/generators/install_generator.rb +209 -0
  69. data/lib/a2a/rails/generators/migration_generator.rb +232 -0
  70. data/lib/a2a/rails/generators/templates/add_a2a_indexes.rb +57 -0
  71. data/lib/a2a/rails/generators/templates/agent_controller.rb +122 -0
  72. data/lib/a2a/rails/generators/templates/agent_controller_spec.rb +160 -0
  73. data/lib/a2a/rails/generators/templates/agent_readme.md +200 -0
  74. data/lib/a2a/rails/generators/templates/create_a2a_push_notification_configs.rb +68 -0
  75. data/lib/a2a/rails/generators/templates/create_a2a_tasks.rb +83 -0
  76. data/lib/a2a/rails/generators/templates/example_agent_controller.rb +228 -0
  77. data/lib/a2a/rails/generators/templates/initializer.rb +108 -0
  78. data/lib/a2a/rails/generators/templates/push_notification_config_model.rb +228 -0
  79. data/lib/a2a/rails/generators/templates/task_model.rb +200 -0
  80. data/lib/a2a/rails/tasks/a2a.rake +228 -0
  81. data/lib/a2a/server/a2a_methods.rb +520 -0
  82. data/lib/a2a/server/agent.rb +537 -0
  83. data/lib/a2a/server/agent_execution/agent_executor.rb +279 -0
  84. data/lib/a2a/server/agent_execution/request_context.rb +219 -0
  85. data/lib/a2a/server/apps/rack_app.rb +311 -0
  86. data/lib/a2a/server/apps/sinatra_app.rb +261 -0
  87. data/lib/a2a/server/default_request_handler.rb +350 -0
  88. data/lib/a2a/server/events/event_consumer.rb +116 -0
  89. data/lib/a2a/server/events/event_queue.rb +226 -0
  90. data/lib/a2a/server/example_agent.rb +248 -0
  91. data/lib/a2a/server/handler.rb +281 -0
  92. data/lib/a2a/server/middleware/authentication_middleware.rb +212 -0
  93. data/lib/a2a/server/middleware/cors_middleware.rb +171 -0
  94. data/lib/a2a/server/middleware/logging_middleware.rb +362 -0
  95. data/lib/a2a/server/middleware/rate_limit_middleware.rb +382 -0
  96. data/lib/a2a/server/middleware.rb +213 -0
  97. data/lib/a2a/server/push_notification_manager.rb +327 -0
  98. data/lib/a2a/server/request_handler.rb +136 -0
  99. data/lib/a2a/server/storage/base.rb +141 -0
  100. data/lib/a2a/server/storage/database.rb +266 -0
  101. data/lib/a2a/server/storage/memory.rb +274 -0
  102. data/lib/a2a/server/storage/redis.rb +320 -0
  103. data/lib/a2a/server/storage.rb +38 -0
  104. data/lib/a2a/server/task_manager.rb +534 -0
  105. data/lib/a2a/transport/grpc.rb +481 -0
  106. data/lib/a2a/transport/http.rb +415 -0
  107. data/lib/a2a/transport/sse.rb +499 -0
  108. data/lib/a2a/types/agent_card.rb +540 -0
  109. data/lib/a2a/types/artifact.rb +99 -0
  110. data/lib/a2a/types/base_model.rb +223 -0
  111. data/lib/a2a/types/events.rb +117 -0
  112. data/lib/a2a/types/message.rb +106 -0
  113. data/lib/a2a/types/part.rb +288 -0
  114. data/lib/a2a/types/push_notification.rb +139 -0
  115. data/lib/a2a/types/security.rb +167 -0
  116. data/lib/a2a/types/task.rb +154 -0
  117. data/lib/a2a/types.rb +88 -0
  118. data/lib/a2a/utils/helpers.rb +245 -0
  119. data/lib/a2a/utils/message_buffer.rb +278 -0
  120. data/lib/a2a/utils/performance.rb +247 -0
  121. data/lib/a2a/utils/rails_detection.rb +97 -0
  122. data/lib/a2a/utils/structured_logger.rb +306 -0
  123. data/lib/a2a/utils/time_helpers.rb +167 -0
  124. data/lib/a2a/utils/validation.rb +8 -0
  125. data/lib/a2a/version.rb +6 -0
  126. data/lib/a2a-rails.rb +58 -0
  127. data/lib/a2a.rb +198 -0
  128. metadata +437 -0
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "erb"
5
+
6
+ module A2A
7
+ class Configuration
8
+ # Module for loading configuration from YAML files
9
+ module FileLoader
10
+ # Load configuration from YAML file
11
+ # @param file_path [String] Path to YAML configuration file
12
+ # @param environment [String, nil] Environment section to load
13
+ # @return [self]
14
+ def load_from_file(file_path, environment: nil)
15
+ @config_file = file_path
16
+ environment ||= @environment
17
+
18
+ # Validate file exists and is readable
19
+ raise A2A::Errors::ConfigurationError, "Configuration file not found: #{file_path}" unless File.exist?(file_path)
20
+
21
+ raise A2A::Errors::ConfigurationError, "Configuration file not readable: #{file_path}" unless File.readable?(file_path)
22
+
23
+ begin
24
+ content = File.read(file_path)
25
+
26
+ # Process ERB if the file contains ERB syntax
27
+ erb_content = if content.include?("<%") || content.include?("%>")
28
+ ERB.new(content).result
29
+ else
30
+ content
31
+ end
32
+
33
+ # Parse YAML with proper error handling
34
+ config_data = YAML.safe_load(erb_content, aliases: true, symbolize_names: false) || {}
35
+
36
+ unless config_data.is_a?(Hash)
37
+ raise A2A::Errors::ConfigurationError,
38
+ "Configuration file must contain a hash/dictionary at root level"
39
+ end
40
+ rescue Psych::SyntaxError => e
41
+ raise A2A::Errors::ConfigurationError,
42
+ "Invalid YAML syntax in configuration file #{file_path}: #{e.message}"
43
+ rescue StandardError => e
44
+ raise A2A::Errors::ConfigurationError,
45
+ "Failed to load configuration file #{file_path}: #{e.message}"
46
+ end
47
+
48
+ # Load environment-specific configuration
49
+ env_config = config_data[environment.to_s] || config_data[environment.to_sym] || {}
50
+
51
+ unless env_config.is_a?(Hash)
52
+ raise A2A::Errors::ConfigurationError,
53
+ "Environment configuration for '#{environment}' must be a hash/dictionary"
54
+ end
55
+
56
+ # Apply configuration from file with validation
57
+ begin
58
+ apply_hash_config(env_config)
59
+ rescue StandardError => e
60
+ raise A2A::Errors::ConfigurationError,
61
+ "Failed to apply configuration from file #{file_path}: #{e.message}"
62
+ end
63
+
64
+ self
65
+ end
66
+
67
+ private
68
+
69
+ # Apply configuration from hash
70
+ def apply_hash_config(config_hash)
71
+ unknown_keys = []
72
+
73
+ config_hash.each do |key, value|
74
+ key_str = key.to_s
75
+ setter = "#{key_str}="
76
+
77
+ if respond_to?(setter)
78
+ begin
79
+ # Special handling for log_level to normalize from string to symbol
80
+ if key_str == "log_level" && value.is_a?(String)
81
+ normalized_value = value.downcase
82
+ valid_levels = %w[debug info warn error fatal]
83
+ value = normalized_value.to_sym if valid_levels.include?(normalized_value)
84
+ end
85
+ send(setter, value)
86
+ rescue StandardError => e
87
+ raise A2A::Errors::ConfigurationError,
88
+ "Failed to set #{key_str}: #{e.message}"
89
+ end
90
+ else
91
+ # Handle nested configurations
92
+ case key_str
93
+ when "redis_config", "redis"
94
+ if value.is_a?(Hash)
95
+ @redis_config = value.transform_keys(&:to_sym)
96
+ elsif value.is_a?(String)
97
+ @redis_config = { url: value }
98
+ else
99
+ raise A2A::Errors::ConfigurationError,
100
+ "redis_config must be a hash or URL string, got: #{value.class}"
101
+ end
102
+ else
103
+ unknown_keys << key_str
104
+ end
105
+ end
106
+ end
107
+
108
+ # Warn about unknown keys but don't fail
109
+ return if unknown_keys.empty?
110
+
111
+ logger&.warn("Unknown configuration keys: #{unknown_keys.join(', ')}")
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ class Configuration
5
+ # Module for configuration inheritance support
6
+ module Inheritance
7
+ # Create a child configuration with inheritance
8
+ # @param **overrides [Hash] Configuration overrides
9
+ # @return [Configuration] New configuration instance
10
+ def child(**overrides)
11
+ # Create new instance without loading defaults to avoid overwriting parent values
12
+ child_config = self.class.allocate
13
+ child_config.instance_variable_set(:@environment, @environment)
14
+ child_config.instance_variable_set(:@parent_config, self)
15
+ child_config.instance_variable_set(:@overrides, overrides)
16
+ child_config.instance_variable_set(:@config_file, nil)
17
+
18
+ # Copy all instance variables from parent to child (deep copy for complex objects)
19
+ instance_variables.each do |var|
20
+ next if %i[@parent_config @overrides @config_file].include?(var)
21
+
22
+ value = instance_variable_get(var)
23
+ # Deep copy arrays and hashes to prevent shared references
24
+ copied_value = case value
25
+ when Array
26
+ value.dup
27
+ when Hash
28
+ value.dup
29
+ else
30
+ value
31
+ end
32
+ child_config.instance_variable_set(var, copied_value)
33
+ end
34
+
35
+ # Apply overrides after copying parent values
36
+ child_config.send(:apply_overrides, overrides)
37
+
38
+ child_config
39
+ end
40
+
41
+ # Get configuration value with inheritance support
42
+ # @param key [Symbol, String] Configuration key
43
+ # @return [Object] Configuration value
44
+ def get(key)
45
+ # Convert key to instance variable name
46
+ ivar_name = "@#{key}"
47
+
48
+ # Check if we have the instance variable set
49
+ if instance_variable_defined?(ivar_name)
50
+ value = instance_variable_get(ivar_name)
51
+ # Return value if it's not nil, or if we don't have a parent
52
+ return value unless value.nil? && @parent_config
53
+ end
54
+
55
+ # Fall back to parent configuration if value is nil and parent exists
56
+ @parent_config&.get(key)
57
+ end
58
+
59
+ # Validate that child configuration properly inherits from parent
60
+ # @return [Boolean] true if inheritance is working correctly
61
+ def validate_inheritance!
62
+ return true unless @parent_config
63
+
64
+ # Check that we can access parent values when child values are nil
65
+ parent_attrs = @parent_config.to_h
66
+ parent_attrs.each do |key, parent_value|
67
+ next if parent_value.nil?
68
+
69
+ # Get child value directly (not through inheritance)
70
+ child_ivar = "@#{key}"
71
+ child_value = instance_variable_defined?(child_ivar) ? instance_variable_get(child_ivar) : nil
72
+
73
+ # If child value is nil, get should return parent value
74
+ next unless child_value.nil?
75
+
76
+ inherited_value = get(key)
77
+ unless inherited_value == parent_value
78
+ raise A2A::Errors::ConfigurationError,
79
+ "Inheritance failed for #{key}: expected #{parent_value.inspect}, got #{inherited_value.inspect}"
80
+ end
81
+ end
82
+
83
+ true
84
+ end
85
+
86
+ # Merge configuration from another configuration object
87
+ # @param other [Configuration] Configuration to merge from
88
+ # @return [self]
89
+ def merge!(other)
90
+ return self unless other.is_a?(A2A::Configuration)
91
+
92
+ other.to_h.each do |key, value|
93
+ setter = "#{key}="
94
+ send(setter, value) if respond_to?(setter)
95
+ end
96
+
97
+ self
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ class Configuration
5
+ # Module for configuration validation
6
+ module Validator
7
+ # Validate the configuration
8
+ # @raise [A2A::Errors::ConfigurationError] if configuration is invalid
9
+ def validate!
10
+ errors = []
11
+
12
+ begin
13
+ validate_basic_config
14
+ rescue A2A::Errors::ConfigurationError => e
15
+ errors << e.message
16
+ end
17
+
18
+ begin
19
+ validate_transport_config
20
+ rescue A2A::Errors::ConfigurationError => e
21
+ errors << e.message
22
+ end
23
+
24
+ if rails_integration
25
+ begin
26
+ validate_rails_config
27
+ rescue A2A::Errors::ConfigurationError => e
28
+ errors << e.message
29
+ end
30
+ end
31
+
32
+ begin
33
+ validate_redis_config
34
+ rescue A2A::Errors::ConfigurationError => e
35
+ errors << e.message
36
+ end
37
+
38
+ begin
39
+ validate_environment_config
40
+ rescue A2A::Errors::ConfigurationError => e
41
+ errors << e.message
42
+ end
43
+
44
+ begin
45
+ validate_timeout_values
46
+ rescue A2A::Errors::ConfigurationError => e
47
+ errors << e.message
48
+ end
49
+
50
+ begin
51
+ validate_boolean_options
52
+ rescue A2A::Errors::ConfigurationError => e
53
+ errors << e.message
54
+ end
55
+
56
+ unless errors.empty?
57
+ raise A2A::Errors::ConfigurationError,
58
+ "Configuration validation failed:\n - #{errors.join("\n - ")}"
59
+ end
60
+
61
+ true
62
+ end
63
+
64
+ private
65
+
66
+ # Validation methods
67
+ def validate_basic_config
68
+ raise A2A::Errors::ConfigurationError, "default_timeout must be positive" if default_timeout <= 0
69
+
70
+ validate_log_level
71
+ validate_protocol_version
72
+ end
73
+
74
+ # Validate log level with proper string/symbol handling
75
+ def validate_log_level
76
+ return if log_level.nil?
77
+
78
+ # Accept both string and symbol log levels, normalize to symbol
79
+ normalized_level = log_level.to_s.downcase
80
+ valid_levels = %w[debug info warn error fatal]
81
+
82
+ unless valid_levels.include?(normalized_level)
83
+ raise A2A::Errors::ConfigurationError,
84
+ "log_level must be one of: #{valid_levels.join(', ')}. Got: #{log_level.inspect}"
85
+ end
86
+
87
+ # Normalize to symbol for internal use
88
+ @log_level = normalized_level.to_sym
89
+ end
90
+
91
+ # Validate protocol version with proper nil/blank handling
92
+ def validate_protocol_version
93
+ return if protocol_version.nil?
94
+
95
+ if protocol_version.respond_to?(:strip) && protocol_version.strip.empty?
96
+ raise A2A::Errors::ConfigurationError,
97
+ "protocol_version cannot be blank"
98
+ elsif protocol_version.respond_to?(:empty?) && protocol_version.empty?
99
+ raise A2A::Errors::ConfigurationError,
100
+ "protocol_version cannot be empty"
101
+ end
102
+ end
103
+
104
+ def validate_transport_config
105
+ valid_transports = %w[JSONRPC GRPC HTTP+JSON]
106
+ unless valid_transports.include?(default_transport)
107
+ raise A2A::Errors::ConfigurationError, "default_transport must be one of: #{valid_transports.join(', ')}"
108
+ end
109
+
110
+ unless default_input_modes.is_a?(Array) && default_input_modes.all?(String)
111
+ raise A2A::Errors::ConfigurationError, "default_input_modes must be an array of strings"
112
+ end
113
+
114
+ return if default_output_modes.is_a?(Array) && default_output_modes.all?(String)
115
+
116
+ raise A2A::Errors::ConfigurationError, "default_output_modes must be an array of strings"
117
+ end
118
+
119
+ def validate_rails_config
120
+ raise A2A::Errors::ConfigurationError, "mount_path must start with '/'" if mount_path && !mount_path.start_with?("/")
121
+
122
+ # Only validate Rails version if Rails is available and version checking is needed
123
+ return unless rails_available? && rails_version_requires_validation?
124
+
125
+ current_version = rails_version
126
+ raise A2A::Errors::ConfigurationError,
127
+ "Rails integration requires Rails 6.0 or higher. Current version: #{current_version}"
128
+ end
129
+
130
+ def validate_redis_config
131
+ raise A2A::Errors::ConfigurationError, "redis_config must be a hash" if redis_config && !redis_config.is_a?(Hash)
132
+
133
+ return unless redis_config && redis_config[:url] && !redis_config[:url].is_a?(String)
134
+
135
+ raise A2A::Errors::ConfigurationError, "redis_config[:url] must be a string"
136
+ end
137
+
138
+ def validate_environment_config
139
+ return if environment.nil?
140
+
141
+ raise A2A::Errors::ConfigurationError, "environment must be a string" unless environment.is_a?(String)
142
+
143
+ valid_environments = %w[development test production staging]
144
+ return if valid_environments.include?(environment)
145
+
146
+ logger&.warn("Unknown environment: #{environment}. Valid environments: #{valid_environments.join(', ')}")
147
+ end
148
+
149
+ def validate_timeout_values
150
+ return unless default_timeout && (!default_timeout.is_a?(Numeric) || default_timeout <= 0)
151
+
152
+ raise A2A::Errors::ConfigurationError,
153
+ "default_timeout must be a positive number, got: #{default_timeout.inspect}"
154
+ end
155
+
156
+ def validate_boolean_options
157
+ boolean_options = {
158
+ streaming_enabled: streaming_enabled,
159
+ push_notifications_enabled: push_notifications_enabled,
160
+ rails_integration: rails_integration,
161
+ auto_mount: auto_mount,
162
+ middleware_enabled: middleware_enabled,
163
+ authentication_required: authentication_required,
164
+ cors_enabled: cors_enabled,
165
+ rate_limiting_enabled: rate_limiting_enabled,
166
+ logging_enabled: logging_enabled,
167
+ webhook_authentication_required: webhook_authentication_required
168
+ }
169
+
170
+ boolean_options.each do |option, value|
171
+ next if value.nil?
172
+ next if [true, false].include?(value)
173
+
174
+ raise A2A::Errors::ConfigurationError,
175
+ "#{option} must be true or false, got: #{value.inspect}"
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "utils/rails_detection"
4
+ require_relative "configuration/defaults"
5
+ require_relative "configuration/environment_loader"
6
+ require_relative "configuration/file_loader"
7
+ require_relative "configuration/validator"
8
+ require_relative "configuration/inheritance"
9
+
10
+ ##
11
+ # Global configuration for the A2A Ruby SDK
12
+ #
13
+ # Supports environment variables, per-environment configuration,
14
+ # configuration inheritance, and validation.
15
+ #
16
+ # @example Configure the SDK programmatically
17
+ # A2A.configure do |config|
18
+ # config.default_timeout = 30
19
+ # config.log_level = :debug
20
+ # config.protocol_version = "0.3.0"
21
+ # end
22
+ #
23
+ # @example Load from environment variables
24
+ # # Set A2A_DEFAULT_TIMEOUT=60 in environment
25
+ # config = A2A::Configuration.new
26
+ # config.default_timeout # => 60
27
+ #
28
+ # @example Load from YAML file
29
+ # A2A.configure_from_file('config/a2a.yml')
30
+ #
31
+ module A2A
32
+ class Configuration
33
+ include A2A::Utils::RailsDetection
34
+ include Defaults
35
+ include EnvironmentLoader
36
+ include FileLoader
37
+ include Validator
38
+ include Inheritance
39
+
40
+ # Default timeout for HTTP requests in seconds
41
+ # @return [Integer]
42
+ attr_accessor :default_timeout
43
+
44
+ # Default log level
45
+ # @return [Symbol]
46
+ attr_accessor :log_level
47
+
48
+ # A2A protocol version to use
49
+ # @return [String]
50
+ attr_accessor :protocol_version
51
+
52
+ # Default transport protocol
53
+ # @return [String]
54
+ attr_accessor :default_transport
55
+
56
+ # Enable/disable streaming support
57
+ # @return [Boolean]
58
+ attr_accessor :streaming_enabled
59
+
60
+ # Enable/disable push notifications
61
+ # @return [Boolean]
62
+ attr_accessor :push_notifications_enabled
63
+
64
+ # Default input MIME types
65
+ # @return [Array<String>]
66
+ attr_accessor :default_input_modes
67
+
68
+ # Default output MIME types
69
+ # @return [Array<String>]
70
+ attr_accessor :default_output_modes
71
+
72
+ # Redis configuration for task storage and rate limiting
73
+ # @return [Hash]
74
+ attr_accessor :redis_config
75
+
76
+ # Enable Rails integration
77
+ # @return [Boolean]
78
+ attr_accessor :rails_integration
79
+
80
+ # Rails mount path for A2A endpoints
81
+ # @return [String]
82
+ attr_accessor :mount_path
83
+
84
+ # Enable automatic mounting of A2A routes
85
+ # @return [Boolean]
86
+ attr_accessor :auto_mount
87
+
88
+ # Enable A2A middleware stack
89
+ # @return [Boolean]
90
+ attr_accessor :middleware_enabled
91
+
92
+ # Require authentication for A2A endpoints
93
+ # @return [Boolean]
94
+ attr_accessor :authentication_required
95
+
96
+ # Enable CORS middleware
97
+ # @return [Boolean]
98
+ attr_accessor :cors_enabled
99
+
100
+ # Enable rate limiting middleware
101
+ # @return [Boolean]
102
+ attr_accessor :rate_limiting_enabled
103
+
104
+ # Enable logging middleware
105
+ # @return [Boolean]
106
+ attr_accessor :logging_enabled
107
+
108
+ # Require authentication for webhook endpoints
109
+ # @return [Boolean]
110
+ attr_accessor :webhook_authentication_required
111
+
112
+ # Custom logger instance
113
+ # @return [Logger, nil]
114
+ attr_accessor :logger
115
+
116
+ # User agent string for HTTP requests
117
+ # @return [String]
118
+ attr_accessor :user_agent
119
+
120
+ # Environment for configuration (development, test, production)
121
+ # @return [String]
122
+ attr_accessor :environment
123
+
124
+ # Configuration file path
125
+ # @return [String, nil]
126
+ attr_accessor :config_file
127
+
128
+ # Parent configuration for inheritance
129
+ # @return [Configuration, nil]
130
+ attr_accessor :parent_config
131
+
132
+ # Configuration overrides
133
+ # @return [Hash]
134
+ attr_accessor :overrides
135
+
136
+ def initialize(environment: nil, parent: nil, **overrides)
137
+ @environment = environment || detect_environment
138
+ @parent_config = parent
139
+ @overrides = overrides
140
+ @config_file = nil
141
+
142
+ load_defaults
143
+ load_from_environment
144
+ apply_overrides(overrides)
145
+ end
146
+
147
+ private
148
+
149
+ # Apply configuration overrides
150
+ def apply_overrides(overrides)
151
+ overrides.each do |key, value|
152
+ setter = "#{key}="
153
+ unless respond_to?(setter, true) # Check for both public and private methods
154
+ raise A2A::Errors::ConfigurationError, "Unknown configuration option: #{key}"
155
+ end
156
+
157
+ send(setter, value)
158
+ end
159
+ end
160
+
161
+ public
162
+
163
+ # Get the logger instance
164
+ # @return [Logger]
165
+ def logger
166
+ @logger ||= if rails_available? && rails_logger
167
+ rails_logger
168
+ else
169
+ require "logger"
170
+ Logger.new($stdout, level: log_level)
171
+ end
172
+ end
173
+
174
+ # Convert configuration to hash
175
+ # @return [Hash] Configuration as hash
176
+ def to_h
177
+ {
178
+ environment: @environment,
179
+ default_timeout: @default_timeout,
180
+ log_level: @log_level,
181
+ protocol_version: @protocol_version,
182
+ default_transport: @default_transport,
183
+ streaming_enabled: @streaming_enabled,
184
+ push_notifications_enabled: @push_notifications_enabled,
185
+ default_input_modes: @default_input_modes,
186
+ default_output_modes: @default_output_modes,
187
+ redis_config: @redis_config,
188
+ rails_integration: @rails_integration,
189
+ mount_path: @mount_path,
190
+ auto_mount: @auto_mount,
191
+ middleware_enabled: @middleware_enabled,
192
+ authentication_required: @authentication_required,
193
+ cors_enabled: @cors_enabled,
194
+ rate_limiting_enabled: @rate_limiting_enabled,
195
+ logging_enabled: @logging_enabled,
196
+ webhook_authentication_required: @webhook_authentication_required,
197
+ user_agent: @user_agent
198
+ }
199
+ end
200
+ end
201
+ end