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,481 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "grpc"
5
+ rescue LoadError
6
+ # gRPC is optional - define a stub implementation
7
+ module GRPC
8
+ class BadStatus < StandardError; end
9
+
10
+ module Core
11
+ class StatusCodes
12
+ OK = 0
13
+ CANCELLED = 1
14
+ UNKNOWN = 2
15
+ INVALID_ARGUMENT = 3
16
+ DEADLINE_EXCEEDED = 4
17
+ NOT_FOUND = 5
18
+ ALREADY_EXISTS = 6
19
+ PERMISSION_DENIED = 7
20
+ RESOURCE_EXHAUSTED = 8
21
+ FAILED_PRECONDITION = 9
22
+ ABORTED = 10
23
+ OUT_OF_RANGE = 11
24
+ UNIMPLEMENTED = 12
25
+ INTERNAL = 13
26
+ UNAVAILABLE = 14
27
+ DATA_LOSS = 15
28
+ UNAUTHENTICATED = 16
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ module A2A
35
+ module Transport
36
+ ##
37
+ # gRPC transport implementation with optional dependency
38
+ # Provides bidirectional streaming support and gRPC-specific error mapping
39
+ #
40
+ class Grpc
41
+ # Check if gRPC is available
42
+ GRPC_AVAILABLE = defined?(::GRPC) && ::GRPC.const_defined?(:ClientStub)
43
+
44
+ # Default configuration values
45
+ DEFAULT_TIMEOUT = 30
46
+ DEFAULT_DEADLINE = 60
47
+ DEFAULT_MAX_RECEIVE_MESSAGE_SIZE = 4 * 1024 * 1024 # 4MB
48
+ DEFAULT_MAX_SEND_MESSAGE_SIZE = 4 * 1024 * 1024 # 4MB
49
+ DEFAULT_KEEPALIVE_TIME = 30
50
+ DEFAULT_KEEPALIVE_TIMEOUT = 5
51
+
52
+ attr_reader :endpoint, :config, :stub, :credentials
53
+
54
+ ##
55
+ # Initialize gRPC transport
56
+ #
57
+ # @param endpoint [String] gRPC endpoint (host:port)
58
+ # @param config [Hash] Configuration options
59
+ # @option config [Integer] :timeout (30) Request timeout in seconds
60
+ # @option config [Integer] :deadline (60) Request deadline in seconds
61
+ # @option config [Integer] :max_receive_message_size (4MB) Max receive message size
62
+ # @option config [Integer] :max_send_message_size (4MB) Max send message size
63
+ # @option config [Integer] :keepalive_time (30) Keepalive time in seconds
64
+ # @option config [Integer] :keepalive_timeout (5) Keepalive timeout in seconds
65
+ # @option config [Boolean] :use_tls (true) Use TLS encryption
66
+ # @option config [String] :ca_file Path to CA certificate file
67
+ # @option config [String] :cert_file Path to client certificate file
68
+ # @option config [String] :key_file Path to client private key file
69
+ # @option config [Hash] :metadata ({}) Default metadata
70
+ # @option config [Object] :credentials Custom credentials object
71
+ #
72
+ def initialize(endpoint, config = {})
73
+ unless GRPC_AVAILABLE
74
+ raise A2A::Errors::TransportError,
75
+ "gRPC is not available. Install the 'grpc' gem to use gRPC transport."
76
+ end
77
+
78
+ @endpoint = endpoint
79
+ @config = default_config.merge(config)
80
+ @credentials = build_credentials
81
+ @stub = nil
82
+ @call_options = build_call_options
83
+ end
84
+
85
+ ##
86
+ # Connect to gRPC service
87
+ #
88
+ # @return [Boolean] Connection success
89
+ # @raise [A2A::Errors::TransportError] On connection errors
90
+ #
91
+ def connect
92
+ return true if connected?
93
+
94
+ begin
95
+ @stub = build_stub
96
+ # Test connection with a simple call
97
+ @stub.class.rpc_descs.keys.first&.tap do |_method|
98
+ # This is a simplified connection test
99
+ end
100
+ true
101
+ rescue StandardError => e
102
+ raise A2A::Errors::TransportError, "Failed to connect to gRPC service: #{e.message}"
103
+ end
104
+ end
105
+
106
+ ##
107
+ # Disconnect from gRPC service
108
+ #
109
+ def disconnect
110
+ @stub = nil
111
+ end
112
+
113
+ ##
114
+ # Check if connected
115
+ #
116
+ # @return [Boolean] Connection status
117
+ #
118
+ def connected?
119
+ !@stub.nil?
120
+ end
121
+
122
+ ##
123
+ # Send unary gRPC request
124
+ #
125
+ # @param method [Symbol] gRPC method name
126
+ # @param request [Object] Request message
127
+ # @param metadata [Hash] Request metadata
128
+ # @param timeout [Integer, nil] Request timeout
129
+ # @return [Object] Response message
130
+ # @raise [A2A::Errors::TransportError] On gRPC errors
131
+ #
132
+ def unary_call(method, request, metadata: {}, timeout: nil)
133
+ ensure_connected!
134
+
135
+ call_options = @call_options.dup
136
+ call_options[:timeout] = timeout if timeout
137
+ call_options[:metadata] = @config[:metadata].merge(metadata)
138
+
139
+ begin
140
+ @stub.public_send(method, request, call_options)
141
+ rescue ::GRPC::BadStatus => e
142
+ raise map_grpc_error(e)
143
+ rescue StandardError => e
144
+ raise A2A::Errors::TransportError, "gRPC call failed: #{e.message}"
145
+ end
146
+ end
147
+
148
+ ##
149
+ # Send client streaming gRPC request
150
+ #
151
+ # @param method [Symbol] gRPC method name
152
+ # @param requests [Enumerator] Request stream
153
+ # @param metadata [Hash] Request metadata
154
+ # @param timeout [Integer, nil] Request timeout
155
+ # @return [Object] Response message
156
+ # @raise [A2A::Errors::TransportError] On gRPC errors
157
+ #
158
+ def client_streaming_call(method, requests, metadata: {}, timeout: nil)
159
+ ensure_connected!
160
+
161
+ call_options = @call_options.dup
162
+ call_options[:timeout] = timeout if timeout
163
+ call_options[:metadata] = @config[:metadata].merge(metadata)
164
+
165
+ begin
166
+ @stub.public_send(method, requests, call_options)
167
+ rescue ::GRPC::BadStatus => e
168
+ raise map_grpc_error(e)
169
+ rescue StandardError => e
170
+ raise A2A::Errors::TransportError, "gRPC streaming call failed: #{e.message}"
171
+ end
172
+ end
173
+
174
+ ##
175
+ # Send server streaming gRPC request
176
+ #
177
+ # @param method [Symbol] gRPC method name
178
+ # @param request [Object] Request message
179
+ # @param metadata [Hash] Request metadata
180
+ # @param timeout [Integer, nil] Request timeout
181
+ # @return [Enumerator] Response stream
182
+ # @raise [A2A::Errors::TransportError] On gRPC errors
183
+ #
184
+ def server_streaming_call(method, request, metadata: {}, timeout: nil)
185
+ ensure_connected!
186
+
187
+ call_options = @call_options.dup
188
+ call_options[:timeout] = timeout if timeout
189
+ call_options[:metadata] = @config[:metadata].merge(metadata)
190
+
191
+ begin
192
+ @stub.public_send(method, request, call_options)
193
+ rescue ::GRPC::BadStatus => e
194
+ raise map_grpc_error(e)
195
+ rescue StandardError => e
196
+ raise A2A::Errors::TransportError, "gRPC streaming call failed: #{e.message}"
197
+ end
198
+ end
199
+
200
+ ##
201
+ # Send bidirectional streaming gRPC request
202
+ #
203
+ # @param method [Symbol] gRPC method name
204
+ # @param requests [Enumerator] Request stream
205
+ # @param metadata [Hash] Request metadata
206
+ # @param timeout [Integer, nil] Request timeout
207
+ # @return [Enumerator] Response stream
208
+ # @raise [A2A::Errors::TransportError] On gRPC errors
209
+ #
210
+ def bidi_streaming_call(method, requests, metadata: {}, timeout: nil)
211
+ ensure_connected!
212
+
213
+ call_options = @call_options.dup
214
+ call_options[:timeout] = timeout if timeout
215
+ call_options[:metadata] = @config[:metadata].merge(metadata)
216
+
217
+ begin
218
+ @stub.public_send(method, requests, call_options)
219
+ rescue ::GRPC::BadStatus => e
220
+ raise map_grpc_error(e)
221
+ rescue StandardError => e
222
+ raise A2A::Errors::TransportError, "gRPC bidirectional streaming call failed: #{e.message}"
223
+ end
224
+ end
225
+
226
+ ##
227
+ # Send A2A message via gRPC
228
+ #
229
+ # @param message [A2A::Types::Message] A2A message
230
+ # @param streaming [Boolean] Use streaming response
231
+ # @param metadata [Hash] Request metadata
232
+ # @return [Object, Enumerator] Response or response stream
233
+ #
234
+ def send_a2a_message(message, streaming: false, metadata: {})
235
+ request = build_a2a_request(message)
236
+
237
+ if streaming
238
+ server_streaming_call(:send_message_stream, request, metadata: metadata)
239
+ else
240
+ unary_call(:send_message, request, metadata: metadata)
241
+ end
242
+ end
243
+
244
+ ##
245
+ # Get A2A task via gRPC
246
+ #
247
+ # @param task_id [String] Task ID
248
+ # @param metadata [Hash] Request metadata
249
+ # @return [Object] Task response
250
+ #
251
+ def get_a2a_task(task_id, metadata: {})
252
+ request = build_task_request(task_id)
253
+ unary_call(:get_task, request, metadata: metadata)
254
+ end
255
+
256
+ ##
257
+ # Cancel A2A task via gRPC
258
+ #
259
+ # @param task_id [String] Task ID
260
+ # @param metadata [Hash] Request metadata
261
+ # @return [Object] Cancellation response
262
+ #
263
+ def cancel_a2a_task(task_id, metadata: {})
264
+ request = build_task_request(task_id)
265
+ unary_call(:cancel_task, request, metadata: metadata)
266
+ end
267
+
268
+ ##
269
+ # Get agent card via gRPC
270
+ #
271
+ # @param metadata [Hash] Request metadata
272
+ # @return [Object] Agent card response
273
+ #
274
+ def get_agent_card(metadata: {})
275
+ request = build_empty_request
276
+ unary_call(:get_agent_card, request, metadata: metadata)
277
+ end
278
+
279
+ private
280
+
281
+ ##
282
+ # Ensure connection is established
283
+ #
284
+ # @raise [A2A::Errors::TransportError] If not connected
285
+ #
286
+ def ensure_connected!
287
+ connect unless connected?
288
+ raise A2A::Errors::TransportError, "Not connected to gRPC service" unless connected?
289
+ end
290
+
291
+ ##
292
+ # Build default configuration
293
+ #
294
+ # @return [Hash] Default configuration
295
+ #
296
+ def default_config
297
+ {
298
+ timeout: DEFAULT_TIMEOUT,
299
+ deadline: DEFAULT_DEADLINE,
300
+ max_receive_message_size: DEFAULT_MAX_RECEIVE_MESSAGE_SIZE,
301
+ max_send_message_size: DEFAULT_MAX_SEND_MESSAGE_SIZE,
302
+ keepalive_time: DEFAULT_KEEPALIVE_TIME,
303
+ keepalive_timeout: DEFAULT_KEEPALIVE_TIMEOUT,
304
+ use_tls: true,
305
+ ca_file: nil,
306
+ cert_file: nil,
307
+ key_file: nil,
308
+ metadata: {},
309
+ credentials: nil
310
+ }
311
+ end
312
+
313
+ ##
314
+ # Build gRPC credentials
315
+ #
316
+ # @return [Object] gRPC credentials
317
+ #
318
+ def build_credentials
319
+ return @config[:credentials] if @config[:credentials]
320
+
321
+ if @config[:use_tls]
322
+ if @config[:ca_file] || @config[:cert_file] || @config[:key_file]
323
+ # Custom TLS credentials
324
+ ca_cert = @config[:ca_file] ? File.read(@config[:ca_file]) : nil
325
+ cert = @config[:cert_file] ? File.read(@config[:cert_file]) : nil
326
+ key = @config[:key_file] ? File.read(@config[:key_file]) : nil
327
+
328
+ ::GRPC::Core::ChannelCredentials.new(ca_cert, key, cert)
329
+ else
330
+ # Default TLS credentials
331
+ ::GRPC::Core::ChannelCredentials.new
332
+ end
333
+ else
334
+ # Insecure credentials
335
+ :this_channel_is_insecure
336
+ end
337
+ end
338
+
339
+ ##
340
+ # Build gRPC stub
341
+ #
342
+ # @return [Object] gRPC stub instance
343
+ #
344
+ def build_stub
345
+ # This would be replaced with actual A2A gRPC service stub
346
+ # For now, we'll create a generic stub interface
347
+ A2AServiceStub.new(@endpoint, @credentials, channel_args: build_channel_args)
348
+ end
349
+
350
+ ##
351
+ # Build channel arguments
352
+ #
353
+ # @return [Hash] Channel arguments
354
+ #
355
+ def build_channel_args
356
+ {
357
+ "grpc.keepalive_time_ms" => @config[:keepalive_time] * 1000,
358
+ "grpc.keepalive_timeout_ms" => @config[:keepalive_timeout] * 1000,
359
+ "grpc.keepalive_permit_without_calls" => 1,
360
+ "grpc.http2.max_pings_without_data" => 0,
361
+ "grpc.http2.min_time_between_pings_ms" => 10_000,
362
+ "grpc.http2.min_ping_interval_without_data_ms" => 300_000,
363
+ "grpc.max_receive_message_length" => @config[:max_receive_message_size],
364
+ "grpc.max_send_message_length" => @config[:max_send_message_size]
365
+ }
366
+ end
367
+
368
+ ##
369
+ # Build call options
370
+ #
371
+ # @return [Hash] Call options
372
+ #
373
+ def build_call_options
374
+ {
375
+ timeout: @config[:timeout],
376
+ deadline: Time.now + @config[:deadline],
377
+ metadata: @config[:metadata]
378
+ }
379
+ end
380
+
381
+ ##
382
+ # Map gRPC error to A2A error
383
+ #
384
+ # @param grpc_error [GRPC::BadStatus] gRPC error
385
+ # @return [A2A::Errors::A2AError] Mapped A2A error
386
+ #
387
+ def map_grpc_error(grpc_error)
388
+ case grpc_error.code
389
+ when ::GRPC::Core::StatusCodes::CANCELLED
390
+ A2A::Errors::TaskNotCancelable.new("Request cancelled: #{grpc_error.details}")
391
+ when ::GRPC::Core::StatusCodes::INVALID_ARGUMENT
392
+ A2A::Errors::InvalidParams.new("Invalid argument: #{grpc_error.details}")
393
+ when ::GRPC::Core::StatusCodes::DEADLINE_EXCEEDED
394
+ A2A::Errors::TimeoutError.new("Deadline exceeded: #{grpc_error.details}")
395
+ when ::GRPC::Core::StatusCodes::NOT_FOUND
396
+ A2A::Errors::TaskNotFound.new("Not found: #{grpc_error.details}")
397
+ when ::GRPC::Core::StatusCodes::PERMISSION_DENIED
398
+ A2A::Errors::AuthorizationFailed.new("Permission denied: #{grpc_error.details}")
399
+ when ::GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED
400
+ A2A::Errors::ResourceExhausted.new("Resource exhausted: #{grpc_error.details}")
401
+ when ::GRPC::Core::StatusCodes::UNIMPLEMENTED
402
+ A2A::Errors::CapabilityNotSupported.new("Unimplemented: #{grpc_error.details}")
403
+ when ::GRPC::Core::StatusCodes::UNAVAILABLE
404
+ A2A::Errors::AgentUnavailable.new("Service unavailable: #{grpc_error.details}")
405
+ when ::GRPC::Core::StatusCodes::UNAUTHENTICATED
406
+ A2A::Errors::AuthenticationRequired.new("Unauthenticated: #{grpc_error.details}")
407
+ else
408
+ A2A::Errors::TransportError.new("gRPC error (#{grpc_error.code}): #{grpc_error.details}")
409
+ end
410
+ end
411
+
412
+ ##
413
+ # Build A2A message request
414
+ #
415
+ # @param message [A2A::Types::Message] A2A message
416
+ # @return [Object] gRPC request object
417
+ #
418
+ def build_a2a_request(message)
419
+ # This would convert A2A::Types::Message to protobuf message
420
+ # For now, return a hash representation
421
+ {
422
+ message: message.to_h
423
+ }
424
+ end
425
+
426
+ ##
427
+ # Build task request
428
+ #
429
+ # @param task_id [String] Task ID
430
+ # @return [Object] gRPC request object
431
+ #
432
+ def build_task_request(task_id)
433
+ {
434
+ task_id: task_id
435
+ }
436
+ end
437
+
438
+ ##
439
+ # Build empty request
440
+ #
441
+ # @return [Object] gRPC request object
442
+ #
443
+ def build_empty_request
444
+ {}
445
+ end
446
+ end
447
+
448
+ ##
449
+ # Stub implementation for A2A gRPC service
450
+ # This would be replaced with generated protobuf stubs
451
+ #
452
+ class A2AServiceStub
453
+ def initialize(endpoint, credentials, channel_args: {})
454
+ @endpoint = endpoint
455
+ @credentials = credentials
456
+ @channel_args = channel_args
457
+ end
458
+
459
+ # Placeholder methods - would be generated from protobuf definitions
460
+ def send_message(_request, _options = {})
461
+ raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
462
+ end
463
+
464
+ def send_message_stream(_request, _options = {})
465
+ raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
466
+ end
467
+
468
+ def get_task(_request, _options = {})
469
+ raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
470
+ end
471
+
472
+ def cancel_task(_request, _options = {})
473
+ raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
474
+ end
475
+
476
+ def get_agent_card(_request, _options = {})
477
+ raise A2A::Errors::CapabilityNotSupported, "gRPC service implementation not available"
478
+ end
479
+ end
480
+ end
481
+ end