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,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "request_context"
4
+ require_relative "../events/event_queue"
5
+
6
+ module A2A
7
+ module Server
8
+ module AgentExecution
9
+ ##
10
+ # Abstract base class for agent executors
11
+ #
12
+ # Agent executors contain the core logic of the agent, executing tasks based on
13
+ # requests and publishing updates to an event queue. This mirrors the Python
14
+ # AgentExecutor interface.
15
+ #
16
+ class AgentExecutor
17
+ ##
18
+ # Execute the agent's logic for a given request context
19
+ #
20
+ # The agent should read necessary information from the context and
21
+ # publish Task or Message events, or TaskStatusUpdateEvent/TaskArtifactUpdateEvent
22
+ # to the event_queue. This method should return once the agent's execution
23
+ # for this request is complete or yields control (e.g., enters an input-required state).
24
+ #
25
+ # @param context [RequestContext] The request context containing the message, task ID, etc.
26
+ # @param event_queue [A2A::Server::Events::EventQueue] The queue to publish events to
27
+ # @abstract Subclasses must implement this method
28
+ def execute(context, event_queue)
29
+ raise NotImplementedError, "Subclasses must implement execute"
30
+ end
31
+
32
+ ##
33
+ # Request the agent to cancel an ongoing task
34
+ #
35
+ # The agent should attempt to stop the task identified by the task_id
36
+ # in the context and publish a TaskStatusUpdateEvent with state 'canceled'
37
+ # to the event_queue.
38
+ #
39
+ # @param context [RequestContext] The request context containing the task ID to cancel
40
+ # @param event_queue [A2A::Server::Events::EventQueue] The queue to publish the cancellation status update to
41
+ # @abstract Subclasses must implement this method
42
+ def cancel(context, event_queue)
43
+ raise NotImplementedError, "Subclasses must implement cancel"
44
+ end
45
+
46
+ protected
47
+
48
+ ##
49
+ # Helper method to publish a task status update event
50
+ #
51
+ # @param event_queue [A2A::Server::Events::EventQueue] The event queue
52
+ # @param task_id [String] The task ID
53
+ # @param context_id [String] The context ID
54
+ # @param status [A2A::Types::TaskStatus] The new task status
55
+ # @param metadata [Hash, nil] Optional metadata
56
+ def publish_task_status_update(event_queue, task_id, context_id, status, metadata = nil)
57
+ event_data = A2A::Types::TaskStatusUpdateEvent.new(
58
+ task_id: task_id,
59
+ context_id: context_id,
60
+ status: status,
61
+ metadata: metadata
62
+ )
63
+
64
+ event = A2A::Server::Events::Event.new(
65
+ type: "task_status_update",
66
+ data: event_data
67
+ )
68
+
69
+ event_queue.publish(event)
70
+ end
71
+
72
+ ##
73
+ # Helper method to publish a task artifact update event
74
+ #
75
+ # @param event_queue [A2A::Server::Events::EventQueue] The event queue
76
+ # @param task_id [String] The task ID
77
+ # @param context_id [String] The context ID
78
+ # @param artifact [A2A::Types::Artifact] The artifact
79
+ # @param append [Boolean] Whether this is an append operation
80
+ # @param metadata [Hash, nil] Optional metadata
81
+ def publish_task_artifact_update(event_queue, task_id, context_id, artifact, append = false, metadata = nil)
82
+ event_data = A2A::Types::TaskArtifactUpdateEvent.new(
83
+ task_id: task_id,
84
+ context_id: context_id,
85
+ artifact: artifact,
86
+ append: append,
87
+ metadata: metadata
88
+ )
89
+
90
+ event = A2A::Server::Events::Event.new(
91
+ type: "task_artifact_update",
92
+ data: event_data
93
+ )
94
+
95
+ event_queue.publish(event)
96
+ end
97
+
98
+ ##
99
+ # Helper method to publish a task object
100
+ #
101
+ # @param event_queue [A2A::Server::Events::EventQueue] The event queue
102
+ # @param task [A2A::Types::Task] The task object
103
+ def publish_task(event_queue, task)
104
+ event = A2A::Server::Events::Event.new(
105
+ type: "task",
106
+ data: task
107
+ )
108
+
109
+ event_queue.publish(event)
110
+ end
111
+
112
+ ##
113
+ # Helper method to publish a message object
114
+ #
115
+ # @param event_queue [A2A::Server::Events::EventQueue] The event queue
116
+ # @param message [A2A::Types::Message] The message object
117
+ def publish_message(event_queue, message)
118
+ event = A2A::Server::Events::Event.new(
119
+ type: "message",
120
+ data: message
121
+ )
122
+
123
+ event_queue.publish(event)
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Simple agent executor implementation
129
+ #
130
+ # A basic implementation that can be used as a starting point for custom agents.
131
+ # Provides default behavior for task creation and status management.
132
+ #
133
+ class SimpleAgentExecutor < AgentExecutor
134
+ def initialize(agent_card: nil, task_manager: nil)
135
+ @agent_card = agent_card
136
+ @task_manager = task_manager
137
+ end
138
+
139
+ ##
140
+ # Execute a request by creating a task and processing the message
141
+ #
142
+ # @param context [RequestContext] The request context
143
+ # @param event_queue [A2A::Server::Events::EventQueue] The event queue
144
+ def execute(context, event_queue)
145
+ # Create or get the task
146
+ task = ensure_task(context)
147
+ publish_task(event_queue, task)
148
+
149
+ # Update task to working state
150
+ working_status = A2A::Types::TaskStatus.new(
151
+ state: A2A::Types::TASK_STATE_WORKING,
152
+ message: "Processing request",
153
+ updated_at: Time.now.utc.iso8601
154
+ )
155
+
156
+ publish_task_status_update(
157
+ event_queue,
158
+ task.id,
159
+ task.context_id,
160
+ working_status
161
+ )
162
+
163
+ # Process the message (delegate to subclass)
164
+ result = process_message(context.message, task, context)
165
+
166
+ # Update task with result
167
+ completed_status = A2A::Types::TaskStatus.new(
168
+ state: A2A::Types::TASK_STATE_COMPLETED,
169
+ result: result,
170
+ updated_at: Time.now.utc.iso8601
171
+ )
172
+
173
+ publish_task_status_update(
174
+ event_queue,
175
+ task.id,
176
+ task.context_id,
177
+ completed_status
178
+ )
179
+ rescue StandardError => e
180
+ # Handle errors by updating task status
181
+ if task
182
+ error_status = A2A::Types::TaskStatus.new(
183
+ state: A2A::Types::TASK_STATE_FAILED,
184
+ error: { message: e.message, type: e.class.name },
185
+ updated_at: Time.now.utc.iso8601
186
+ )
187
+
188
+ publish_task_status_update(
189
+ event_queue,
190
+ task.id,
191
+ task.context_id,
192
+ error_status
193
+ )
194
+ end
195
+
196
+ raise
197
+ end
198
+
199
+ ##
200
+ # Cancel a task by updating its status
201
+ #
202
+ # @param context [RequestContext] The request context
203
+ # @param event_queue [A2A::Server::Events::EventQueue] The event queue
204
+ def cancel(context, event_queue)
205
+ task_id = context.task_id
206
+ return unless task_id
207
+
208
+ canceled_status = A2A::Types::TaskStatus.new(
209
+ state: A2A::Types::TASK_STATE_CANCELED,
210
+ message: "Task canceled by request",
211
+ updated_at: Time.now.utc.iso8601
212
+ )
213
+
214
+ publish_task_status_update(
215
+ event_queue,
216
+ task_id,
217
+ context.context_id,
218
+ canceled_status
219
+ )
220
+ end
221
+
222
+ protected
223
+
224
+ ##
225
+ # Process a message (to be implemented by subclasses)
226
+ #
227
+ # @param message [A2A::Types::Message] The message to process
228
+ # @param task [A2A::Types::Task] The associated task
229
+ # @param context [RequestContext] The request context
230
+ # @return [Object] Processing result
231
+ def process_message(message, _task, _context)
232
+ # Default implementation just echoes the message
233
+ {
234
+ echo: message.to_h,
235
+ processed_at: Time.now.utc.iso8601
236
+ }
237
+ end
238
+
239
+ private
240
+
241
+ ##
242
+ # Ensure a task exists for the request context
243
+ #
244
+ # @param context [RequestContext] The request context
245
+ # @return [A2A::Types::Task] The task object
246
+ def ensure_task(context)
247
+ if context.task_id && @task_manager
248
+ # Try to get existing task
249
+ existing_task = begin
250
+ @task_manager.get_task(context.task_id)
251
+ rescue StandardError
252
+ nil
253
+ end
254
+ return existing_task if existing_task
255
+ end
256
+
257
+ # Create new task
258
+ task_id = context.task_id || SecureRandom.uuid
259
+ context_id = context.context_id || SecureRandom.uuid
260
+
261
+ A2A::Types::Task.new(
262
+ id: task_id,
263
+ context_id: context_id,
264
+ status: A2A::Types::TaskStatus.new(
265
+ state: A2A::Types::TASK_STATE_SUBMITTED,
266
+ message: "Task created",
267
+ updated_at: Time.now.utc.iso8601
268
+ ),
269
+ history: context.message ? [context.message] : [],
270
+ metadata: {
271
+ created_at: Time.now.utc.iso8601,
272
+ executor: self.class.name
273
+ }
274
+ )
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,219 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Server
5
+ module AgentExecution
6
+ ##
7
+ # Request context for agent execution
8
+ #
9
+ # Contains all the information needed for an agent to process a request,
10
+ # including the message, task ID, context ID, and server call context.
11
+ #
12
+ class RequestContext
13
+ attr_reader :message, :task_id, :context_id, :server_context, :metadata
14
+
15
+ ##
16
+ # Initialize a new request context
17
+ #
18
+ # @param message [A2A::Types::Message, nil] The message that initiated the request
19
+ # @param task_id [String, nil] The task ID if continuing an existing task
20
+ # @param context_id [String, nil] The context ID for the conversation
21
+ # @param server_context [A2A::Server::Context, nil] The server call context
22
+ # @param metadata [Hash] Additional metadata for the request
23
+ def initialize(message: nil, task_id: nil, context_id: nil, server_context: nil, metadata: {})
24
+ @message = message
25
+ @task_id = task_id
26
+ @context_id = context_id || message&.context_id
27
+ @server_context = server_context
28
+ @metadata = metadata.dup
29
+ end
30
+
31
+ ##
32
+ # Check if this is a new task (no task_id provided)
33
+ #
34
+ # @return [Boolean] True if this is a new task
35
+ def new_task?
36
+ @task_id.nil?
37
+ end
38
+
39
+ ##
40
+ # Check if this is continuing an existing task
41
+ #
42
+ # @return [Boolean] True if continuing an existing task
43
+ def continuing_task?
44
+ !@task_id.nil?
45
+ end
46
+
47
+ ##
48
+ # Check if this request has a message
49
+ #
50
+ # @return [Boolean] True if a message is present
51
+ def has_message?
52
+ !@message.nil?
53
+ end
54
+
55
+ ##
56
+ # Get the user from the server context if available
57
+ #
58
+ # @return [Object, nil] The user object or nil
59
+ def user
60
+ @server_context&.user
61
+ end
62
+
63
+ ##
64
+ # Check if the request is authenticated
65
+ #
66
+ # @return [Boolean] True if authenticated
67
+ def authenticated?
68
+ @server_context&.authenticated? || false
69
+ end
70
+
71
+ ##
72
+ # Get authentication data for a specific scheme
73
+ #
74
+ # @param scheme [String] The authentication scheme
75
+ # @return [Object, nil] Authentication data or nil
76
+ def authentication(scheme)
77
+ @server_context&.get_authentication(scheme)
78
+ end
79
+
80
+ ##
81
+ # Get metadata value
82
+ #
83
+ # @param key [String, Symbol] The metadata key
84
+ # @return [Object, nil] The metadata value or nil
85
+ def get_metadata(key)
86
+ @metadata[key] || @server_context&.get_metadata(key)
87
+ end
88
+
89
+ ##
90
+ # Set metadata value
91
+ #
92
+ # @param key [String, Symbol] The metadata key
93
+ # @param value [Object] The metadata value
94
+ def set_metadata(key, value)
95
+ @metadata[key] = value
96
+ end
97
+
98
+ ##
99
+ # Convert to hash representation
100
+ #
101
+ # @return [Hash] Hash representation of the context
102
+ def to_h
103
+ {
104
+ message: @message&.to_h,
105
+ task_id: @task_id,
106
+ context_id: @context_id,
107
+ metadata: @metadata,
108
+ authenticated: authenticated?,
109
+ user: user&.to_s
110
+ }
111
+ end
112
+
113
+ ##
114
+ # Create a copy of this context with modifications
115
+ #
116
+ # @param **changes [Hash] Changes to apply
117
+ # @return [RequestContext] New context with changes applied
118
+ def with(**changes)
119
+ RequestContext.new(
120
+ message: changes[:message] || @message,
121
+ task_id: changes[:task_id] || @task_id,
122
+ context_id: changes[:context_id] || @context_id,
123
+ server_context: changes[:server_context] || @server_context,
124
+ metadata: @metadata.merge(changes[:metadata] || {})
125
+ )
126
+ end
127
+ end
128
+
129
+ ##
130
+ # Builder for creating request contexts from various inputs
131
+ #
132
+ # Provides a convenient way to build RequestContext objects from
133
+ # different types of input (JSON-RPC requests, HTTP requests, etc.)
134
+ #
135
+ class RequestContextBuilder
136
+ ##
137
+ # Build a request context from a message send request
138
+ #
139
+ # @param params [Hash] The message send parameters
140
+ # @param server_context [A2A::Server::Context, nil] The server context
141
+ # @return [RequestContext] The built request context
142
+ def self.from_message_send(params, server_context = nil)
143
+ message_data = params["message"] || params[:message]
144
+ task_id = params["taskId"] || params[:task_id]
145
+ context_id = params["contextId"] || params[:context_id]
146
+
147
+ message = if message_data.is_a?(A2A::Types::Message)
148
+ message_data
149
+ elsif message_data.is_a?(Hash)
150
+ A2A::Types::Message.from_h(message_data)
151
+ else
152
+ nil
153
+ end
154
+
155
+ # Extract context_id from message if not provided in params
156
+ context_id ||= message&.context_id
157
+
158
+ RequestContext.new(
159
+ message: message,
160
+ task_id: task_id,
161
+ context_id: context_id,
162
+ server_context: server_context,
163
+ metadata: {
164
+ request_type: "message_send",
165
+ params: params
166
+ }
167
+ )
168
+ end
169
+
170
+ ##
171
+ # Build a request context from a task operation request
172
+ #
173
+ # @param params [Hash] The task operation parameters
174
+ # @param server_context [A2A::Server::Context, nil] The server context
175
+ # @param operation [String] The operation type (e.g., 'get', 'cancel')
176
+ # @return [RequestContext] The built request context
177
+ def self.from_task_operation(params, server_context = nil, operation: "get")
178
+ task_id = params["id"] || params[:id] || params["taskId"] || params[:task_id]
179
+ context_id = params["contextId"] || params[:context_id]
180
+
181
+ RequestContext.new(
182
+ task_id: task_id,
183
+ context_id: context_id,
184
+ server_context: server_context,
185
+ metadata: {
186
+ request_type: "task_#{operation}",
187
+ params: params
188
+ }
189
+ )
190
+ end
191
+
192
+ ##
193
+ # Build a request context from a streaming message request
194
+ #
195
+ # @param params [Hash] The streaming message parameters
196
+ # @param server_context [A2A::Server::Context, nil] The server context
197
+ # @return [RequestContext] The built request context
198
+ def self.from_streaming_message(params, server_context = nil)
199
+ context = from_message_send(params, server_context)
200
+ context.set_metadata(:streaming, true)
201
+ context.set_metadata(:request_type, "message_stream")
202
+ context
203
+ end
204
+
205
+ ##
206
+ # Build a request context from a task resubscription request
207
+ #
208
+ # @param params [Hash] The resubscription parameters
209
+ # @param server_context [A2A::Server::Context, nil] The server context
210
+ # @return [RequestContext] The built request context
211
+ def self.from_task_resubscription(params, server_context = nil)
212
+ context = from_task_operation(params, server_context, operation: "resubscribe")
213
+ context.set_metadata(:streaming, true)
214
+ context
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end