a2a-rb 0.1.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 (72) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +38 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +311 -0
  5. data/lib/a2a/agent_capabilities.rb +32 -0
  6. data/lib/a2a/agent_card/builder.rb +135 -0
  7. data/lib/a2a/agent_card/signature.rb +35 -0
  8. data/lib/a2a/agent_card/verifier.rb +19 -0
  9. data/lib/a2a/agent_card.rb +118 -0
  10. data/lib/a2a/agent_extension.rb +32 -0
  11. data/lib/a2a/agent_interface.rb +54 -0
  12. data/lib/a2a/agent_provider.rb +20 -0
  13. data/lib/a2a/agent_skill.rb +46 -0
  14. data/lib/a2a/artifact.rb +40 -0
  15. data/lib/a2a/client.rb +109 -0
  16. data/lib/a2a/discovery.rb +49 -0
  17. data/lib/a2a/json_rpc_envelope.rb +32 -0
  18. data/lib/a2a/message.rb +54 -0
  19. data/lib/a2a/oauth_flow/authorization_code.rb +37 -0
  20. data/lib/a2a/oauth_flow/client_credentials.rb +31 -0
  21. data/lib/a2a/oauth_flow/device_code.rb +34 -0
  22. data/lib/a2a/oauth_flow.rb +37 -0
  23. data/lib/a2a/operation/cancel_task.rb +39 -0
  24. data/lib/a2a/operation/create_task_push_notification_config.rb +38 -0
  25. data/lib/a2a/operation/delete_task_push_notification_config.rb +38 -0
  26. data/lib/a2a/operation/executable.rb +27 -0
  27. data/lib/a2a/operation/get_extended_agent_card.rb +32 -0
  28. data/lib/a2a/operation/get_task.rb +39 -0
  29. data/lib/a2a/operation/get_task_push_notification_config.rb +38 -0
  30. data/lib/a2a/operation/list_task_push_notification_configs.rb +64 -0
  31. data/lib/a2a/operation/list_tasks.rb +78 -0
  32. data/lib/a2a/operation/send_message/configuration.rb +39 -0
  33. data/lib/a2a/operation/send_message.rb +58 -0
  34. data/lib/a2a/operation/send_message_request.rb +37 -0
  35. data/lib/a2a/operation/send_streaming_message.rb +53 -0
  36. data/lib/a2a/operation/subscribe_to_task.rb +40 -0
  37. data/lib/a2a/operation.rb +19 -0
  38. data/lib/a2a/part/data.rb +34 -0
  39. data/lib/a2a/part/file.rb +45 -0
  40. data/lib/a2a/part/text.rb +34 -0
  41. data/lib/a2a/part.rb +21 -0
  42. data/lib/a2a/protocol/http_json/transport.rb +82 -0
  43. data/lib/a2a/protocol/http_json.rb +53 -0
  44. data/lib/a2a/protocol/json_rpc/transport.rb +54 -0
  45. data/lib/a2a/protocol/json_rpc.rb +55 -0
  46. data/lib/a2a/push_notification/authentication_info.rb +29 -0
  47. data/lib/a2a/push_notification/config.rb +40 -0
  48. data/lib/a2a/push_notification/dispatcher.rb +52 -0
  49. data/lib/a2a/push_notification/receiver.rb +54 -0
  50. data/lib/a2a/push_notification.rb +11 -0
  51. data/lib/a2a/role.rb +13 -0
  52. data/lib/a2a/security_requirement.rb +19 -0
  53. data/lib/a2a/security_scheme/api_key.rb +33 -0
  54. data/lib/a2a/security_scheme/http_auth.rb +33 -0
  55. data/lib/a2a/security_scheme/mutual_tls.rb +25 -0
  56. data/lib/a2a/security_scheme/oauth2.rb +52 -0
  57. data/lib/a2a/security_scheme/open_id_connect.rb +30 -0
  58. data/lib/a2a/security_scheme.rb +26 -0
  59. data/lib/a2a/streaming/artifact_update_event.rb +40 -0
  60. data/lib/a2a/streaming/response.rb +65 -0
  61. data/lib/a2a/streaming/sse_parser.rb +43 -0
  62. data/lib/a2a/streaming/sse_writer.rb +25 -0
  63. data/lib/a2a/streaming/status_update_event.rb +43 -0
  64. data/lib/a2a/streaming/subscription.rb +56 -0
  65. data/lib/a2a/streaming.rb +12 -0
  66. data/lib/a2a/task/state.rb +31 -0
  67. data/lib/a2a/task/status.rb +35 -0
  68. data/lib/a2a/task.rb +66 -0
  69. data/lib/a2a/version.rb +6 -0
  70. data/lib/a2a/versioning.rb +28 -0
  71. data/lib/a2a.rb +90 -0
  72. metadata +116 -0
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "oauth_flow/authorization_code"
4
+ require_relative "oauth_flow/client_credentials"
5
+ require_relative "oauth_flow/device_code"
6
+
7
+ module A2A
8
+ module OAuthFlow
9
+ FLOW_KEYS = %w[authorizationCode clientCredentials deviceCode].freeze
10
+ DEPRECATED_FLOW_KEYS = %w[implicit password].freeze
11
+
12
+ def self.from_h(hash)
13
+ reject_deprecated!(hash)
14
+ matched = FLOW_KEYS.count { |k| hash.key?(k) }
15
+ raise ArgumentError, "OAuthFlows must contain exactly one flow type, got #{matched}" unless matched == 1
16
+
17
+ build_flows(hash)
18
+ end
19
+
20
+ def self.build_flows(hash)
21
+ {}.tap do |flows|
22
+ flows[:authorization_code] = AuthorizationCode.from_h(hash["authorizationCode"]) if hash["authorizationCode"]
23
+ flows[:client_credentials] = ClientCredentials.from_h(hash["clientCredentials"]) if hash["clientCredentials"]
24
+ flows[:device_code] = DeviceCode.from_h(hash["deviceCode"]) if hash["deviceCode"]
25
+ end
26
+ end
27
+
28
+ def self.reject_deprecated!(hash)
29
+ deprecated = DEPRECATED_FLOW_KEYS.select { |k| hash.key?(k) }
30
+ return unless deprecated.any?
31
+
32
+ raise ArgumentError,
33
+ "OAuthFlows contains deprecated flow(s): #{deprecated.join(', ')} — use authorizationCode or deviceCode"
34
+ end
35
+ private_class_method :build_flows, :reject_deprecated!
36
+ end
37
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class CancelTask
6
+ include Executable
7
+
8
+ METHOD = "CancelTask"
9
+
10
+ attr_reader :id, :metadata, :tenant
11
+
12
+ def initialize(id:, metadata: nil, tenant: nil)
13
+ @id = id
14
+ @metadata = metadata
15
+ @tenant = tenant
16
+ end
17
+
18
+ def execute_json_rpc(protocol)
19
+ raw = protocol.post(METHOD, params)
20
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
21
+
22
+ Task.from_h(Hash(raw["result"]))
23
+ end
24
+
25
+ def execute_http_json(protocol)
26
+ body = metadata ? { "metadata" => metadata } : {}
27
+ Task.from_h(protocol.post("/tasks/#{id}:cancel", body: body))
28
+ end
29
+
30
+ def params
31
+ {
32
+ "id" => id,
33
+ "metadata" => metadata,
34
+ "tenant" => tenant
35
+ }.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class CreateTaskPushNotificationConfig
6
+ include Executable
7
+
8
+ METHOD = "CreateTaskPushNotificationConfig"
9
+
10
+ attr_reader :config, :tenant
11
+
12
+ def initialize(config, tenant: nil)
13
+ @config = config.is_a?(PushNotification::Config) ? config : PushNotification::Config.from_h(config)
14
+ @tenant = tenant
15
+ end
16
+
17
+ def execute_json_rpc(protocol)
18
+ raw = protocol.post(METHOD, params)
19
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
20
+
21
+ PushNotification::Config.from_h(Hash(raw["result"]))
22
+ end
23
+
24
+ def execute_http_json(protocol)
25
+ PushNotification::Config.from_h(
26
+ protocol.post("/tasks/#{config.task_id}/pushNotificationConfigs", body: config.to_h)
27
+ )
28
+ end
29
+
30
+ def params
31
+ {
32
+ "pushNotificationConfig" => config.to_h,
33
+ "tenant" => tenant
34
+ }.compact
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class DeleteTaskPushNotificationConfig
6
+ include Executable
7
+
8
+ METHOD = "DeleteTaskPushNotificationConfig"
9
+
10
+ attr_reader :task_id, :id, :tenant
11
+
12
+ def initialize(task_id:, id:, tenant: nil)
13
+ @task_id = task_id
14
+ @id = id
15
+ @tenant = tenant
16
+ end
17
+
18
+ def execute_json_rpc(protocol)
19
+ raw = protocol.post(METHOD, params)
20
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
21
+
22
+ nil
23
+ end
24
+
25
+ def execute_http_json(protocol)
26
+ protocol.delete("/tasks/#{task_id}/pushNotificationConfigs/#{id}")
27
+ end
28
+
29
+ def params
30
+ {
31
+ "taskId" => task_id,
32
+ "id" => id,
33
+ "tenant" => tenant
34
+ }.compact
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ module Executable
6
+ def execute(protocol, &)
7
+ if protocol.is_a?(Protocol::HttpJson)
8
+ execute_http_json(protocol, &)
9
+ else
10
+ execute_json_rpc(protocol, &)
11
+ end
12
+ end
13
+
14
+ def execute_json_rpc(*)
15
+ raise NotImplementedError, "#{self.class}#execute_json_rpc is not implemented"
16
+ end
17
+
18
+ def execute_http_json(*)
19
+ raise NotImplementedError, "#{self.class}#execute_http_json is not implemented"
20
+ end
21
+
22
+ def params
23
+ raise NotImplementedError, "#{self.class}#params is not implemented"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class GetExtendedAgentCard
6
+ include Executable
7
+
8
+ METHOD = "GetExtendedAgentCard"
9
+
10
+ attr_reader :tenant
11
+
12
+ def initialize(tenant: nil)
13
+ @tenant = tenant
14
+ end
15
+
16
+ def execute_json_rpc(protocol)
17
+ raw = protocol.post(METHOD, params)
18
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
19
+
20
+ AgentCard.from_h(Hash(raw["result"]))
21
+ end
22
+
23
+ def execute_http_json(protocol)
24
+ AgentCard.from_h(protocol.get("/extendedAgentCard", query: {}))
25
+ end
26
+
27
+ def params
28
+ { "tenant" => tenant }.compact
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class GetTask
6
+ include Executable
7
+
8
+ METHOD = "GetTask"
9
+
10
+ attr_reader :id, :history_length, :tenant
11
+
12
+ def initialize(id:, history_length: nil, tenant: nil)
13
+ @id = id
14
+ @history_length = history_length
15
+ @tenant = tenant
16
+ end
17
+
18
+ def execute_json_rpc(protocol)
19
+ raw = protocol.post(METHOD, params)
20
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
21
+
22
+ Task.from_h(Hash(raw["result"]))
23
+ end
24
+
25
+ def execute_http_json(protocol)
26
+ query = { "historyLength" => history_length }.compact
27
+ Task.from_h(protocol.get("/tasks/#{id}", query: query))
28
+ end
29
+
30
+ def params
31
+ {
32
+ "id" => id,
33
+ "historyLength" => history_length,
34
+ "tenant" => tenant
35
+ }.compact
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class GetTaskPushNotificationConfig
6
+ include Executable
7
+
8
+ METHOD = "GetTaskPushNotificationConfig"
9
+
10
+ attr_reader :task_id, :id, :tenant
11
+
12
+ def initialize(task_id:, id:, tenant: nil)
13
+ @task_id = task_id
14
+ @id = id
15
+ @tenant = tenant
16
+ end
17
+
18
+ def execute_json_rpc(protocol)
19
+ raw = protocol.post(METHOD, params)
20
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
21
+
22
+ PushNotification::Config.from_h(Hash(raw["result"]))
23
+ end
24
+
25
+ def execute_http_json(protocol)
26
+ PushNotification::Config.from_h(protocol.get("/tasks/#{task_id}/pushNotificationConfigs/#{id}", query: {}))
27
+ end
28
+
29
+ def params
30
+ {
31
+ "taskId" => task_id,
32
+ "id" => id,
33
+ "tenant" => tenant
34
+ }.compact
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class ListTaskPushNotificationConfigs
6
+ include Executable
7
+
8
+ METHOD = "ListTaskPushNotificationConfigs"
9
+
10
+ attr_reader :task_id, :page_size, :page_token, :tenant
11
+
12
+ def initialize(task_id:, page_size: nil, page_token: nil, tenant: nil)
13
+ @task_id = task_id
14
+ @page_size = page_size
15
+ @page_token = page_token
16
+ @tenant = tenant
17
+ end
18
+
19
+ def execute_json_rpc(protocol)
20
+ raw = protocol.post(METHOD, params)
21
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
22
+
23
+ Response.from_h(Hash(raw["result"]))
24
+ end
25
+
26
+ def execute_http_json(protocol)
27
+ query = { "pageSize" => page_size, "pageToken" => page_token }.compact
28
+ Response.from_h(protocol.get("/tasks/#{task_id}/pushNotificationConfigs", query: query))
29
+ end
30
+
31
+ def params
32
+ {
33
+ "taskId" => task_id,
34
+ "pageSize" => page_size,
35
+ "pageToken" => page_token,
36
+ "tenant" => tenant
37
+ }.compact
38
+ end
39
+
40
+ class Response
41
+ attr_reader :configs, :next_page_token
42
+
43
+ def initialize(configs:, next_page_token: nil)
44
+ @configs = configs
45
+ @next_page_token = next_page_token
46
+ end
47
+
48
+ def self.from_h(hash)
49
+ new(
50
+ configs: Array(hash["configs"]).map { PushNotification::Config.from_h(_1) },
51
+ next_page_token: hash["nextPageToken"]
52
+ )
53
+ end
54
+
55
+ def to_h
56
+ {
57
+ "configs" => configs.map(&:to_h),
58
+ "nextPageToken" => next_page_token
59
+ }.compact
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class ListTasks
6
+ include Executable
7
+
8
+ METHOD = "ListTasks"
9
+
10
+ attr_reader :context_id, :status, :page_size, :page_token, :history_length,
11
+ :status_timestamp_after, :include_artifacts, :tenant
12
+
13
+ def initialize(**kwargs)
14
+ @context_id = kwargs[:context_id]
15
+ @status = kwargs[:status]
16
+ @page_size = kwargs[:page_size]
17
+ @page_token = kwargs[:page_token]
18
+ @history_length = kwargs[:history_length]
19
+ @status_timestamp_after = kwargs[:status_timestamp_after]
20
+ @include_artifacts = kwargs[:include_artifacts]
21
+ @tenant = kwargs[:tenant]
22
+ end
23
+
24
+ def execute_json_rpc(protocol)
25
+ raw = protocol.post(METHOD, params)
26
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
27
+
28
+ Response.from_h(Hash(raw["result"]))
29
+ end
30
+
31
+ def execute_http_json(protocol)
32
+ Response.from_h(protocol.get("/tasks", query: params))
33
+ end
34
+
35
+ def params
36
+ {
37
+ "contextId" => context_id,
38
+ "status" => status,
39
+ "pageSize" => page_size,
40
+ "pageToken" => page_token,
41
+ "historyLength" => history_length,
42
+ "statusTimestampAfter" => status_timestamp_after,
43
+ "includeArtifacts" => include_artifacts,
44
+ "tenant" => tenant
45
+ }.compact
46
+ end
47
+
48
+ class Response
49
+ attr_reader :tasks, :next_page_token, :page_size, :total_size
50
+
51
+ def initialize(tasks:, next_page_token:, page_size:, total_size:)
52
+ @tasks = tasks
53
+ @next_page_token = next_page_token
54
+ @page_size = page_size
55
+ @total_size = total_size
56
+ end
57
+
58
+ def self.from_h(hash)
59
+ new(
60
+ tasks: Array(hash["tasks"]).map { Task.from_h(_1) },
61
+ next_page_token: hash.fetch("nextPageToken"),
62
+ page_size: hash.fetch("pageSize"),
63
+ total_size: hash.fetch("totalSize")
64
+ )
65
+ end
66
+
67
+ def to_h
68
+ {
69
+ "tasks" => tasks.map(&:to_h),
70
+ "nextPageToken" => next_page_token,
71
+ "pageSize" => page_size,
72
+ "totalSize" => total_size
73
+ }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class SendMessage
6
+ class Configuration
7
+ attr_reader :accepted_output_modes, :task_push_notification_config, :history_length,
8
+ :return_immediately
9
+
10
+ def initialize(**kwargs)
11
+ @accepted_output_modes = kwargs[:accepted_output_modes]
12
+ @task_push_notification_config = kwargs[:task_push_notification_config]
13
+ @history_length = kwargs[:history_length]
14
+ @return_immediately = kwargs[:return_immediately]
15
+ end
16
+
17
+ def self.from_h(hash)
18
+ new(
19
+ accepted_output_modes: hash["acceptedOutputModes"],
20
+ task_push_notification_config: hash["taskPushNotificationConfig"],
21
+ history_length: hash["historyLength"],
22
+ return_immediately: hash["returnImmediately"]
23
+ )
24
+ end
25
+
26
+ def to_h
27
+ push_config = task_push_notification_config
28
+ push_config = push_config.to_h if push_config.is_a?(PushNotification::Config)
29
+ {
30
+ "acceptedOutputModes" => accepted_output_modes,
31
+ "taskPushNotificationConfig" => push_config,
32
+ "historyLength" => history_length,
33
+ "returnImmediately" => return_immediately
34
+ }.compact
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "send_message/configuration"
4
+
5
+ module A2A
6
+ module Operation
7
+ class SendMessage
8
+ include Executable
9
+
10
+ METHOD = "SendMessage"
11
+
12
+ attr_reader :message, :configuration, :metadata, :tenant
13
+
14
+ def initialize(message, configuration: {}, metadata: nil, tenant: nil)
15
+ @message = message.is_a?(Message) ? message : Message.from_h(message)
16
+ @configuration = if configuration.is_a?(Configuration)
17
+ configuration
18
+ else
19
+ Configuration.new(**configuration.transform_keys(&:to_sym))
20
+ end
21
+ @metadata = metadata
22
+ @tenant = tenant
23
+ end
24
+
25
+ def execute_json_rpc(protocol)
26
+ raw = protocol.post(METHOD, params)
27
+ raise A2A.from_json_rpc_error(raw["error"]) if raw["error"]
28
+
29
+ parse_result(Hash(raw["result"]))
30
+ end
31
+
32
+ def execute_http_json(protocol)
33
+ parse_result(protocol.post("/message:send", body: params))
34
+ end
35
+
36
+ def params
37
+ p = { "message" => message.to_h }
38
+ config_h = configuration.to_h
39
+ p["configuration"] = config_h unless config_h.empty?
40
+ p["metadata"] = metadata if metadata
41
+ p["tenant"] = tenant if tenant
42
+ p
43
+ end
44
+
45
+ private
46
+
47
+ def parse_result(result)
48
+ if result.key?("task")
49
+ Task.from_h(result["task"])
50
+ elsif result.key?("message")
51
+ Message.from_h(result["message"])
52
+ else
53
+ raise InvalidAgentResponseError, "response is neither Task nor Message"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "send_message/configuration"
4
+
5
+ module A2A
6
+ module Operation
7
+ # Server-side counterpart to SendMessage. Deserialises the params hash from
8
+ # an incoming SendMessage / SendStreamingMessage JSON-RPC call or HTTP+JSON
9
+ # request body into typed objects.
10
+ class SendMessageRequest
11
+ attr_reader :message, :configuration, :metadata, :tenant
12
+
13
+ def initialize(message:, configuration: nil, metadata: nil, tenant: nil)
14
+ @message = message
15
+ @configuration = configuration
16
+ @metadata = metadata
17
+ @tenant = tenant
18
+ end
19
+
20
+ def self.from_h(hash)
21
+ config_raw = hash["configuration"]
22
+ config = if config_raw
23
+ SendMessage::Configuration.from_h(config_raw)
24
+ else
25
+ SendMessage::Configuration.new
26
+ end
27
+
28
+ new(
29
+ message: Message.from_h(hash.fetch("message")),
30
+ configuration: config,
31
+ metadata: hash["metadata"],
32
+ tenant: hash["tenant"]
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class SendStreamingMessage
6
+ include Executable
7
+
8
+ METHOD = "SendStreamingMessage"
9
+
10
+ Configuration = SendMessage::Configuration
11
+
12
+ attr_reader :message, :configuration, :metadata, :tenant
13
+
14
+ def initialize(message, configuration: {}, metadata: nil, tenant: nil)
15
+ @message = message.is_a?(Message) ? message : Message.from_h(message)
16
+ @configuration = if configuration.is_a?(Configuration)
17
+ configuration
18
+ else
19
+ Configuration.new(**configuration.transform_keys(&:to_sym))
20
+ end
21
+ @metadata = metadata
22
+ @tenant = tenant
23
+ end
24
+
25
+ def execute_json_rpc(protocol, &block)
26
+ protocol.stream(METHOD, params) do |response|
27
+ sub = Streaming::Subscription.new(response)
28
+ return sub unless block
29
+
30
+ sub.each(&block)
31
+ end
32
+ end
33
+
34
+ def execute_http_json(protocol, &block)
35
+ protocol.stream("/message:stream", method: :post, body: params) do |response|
36
+ sub = Streaming::Subscription.new(response)
37
+ return sub unless block
38
+
39
+ sub.each(&block)
40
+ end
41
+ end
42
+
43
+ def params
44
+ p = { "message" => message.to_h }
45
+ config_h = configuration.to_h
46
+ p["configuration"] = config_h unless config_h.empty?
47
+ p["metadata"] = metadata if metadata
48
+ p["tenant"] = tenant if tenant
49
+ p
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module A2A
4
+ module Operation
5
+ class SubscribeToTask
6
+ include Executable
7
+
8
+ METHOD = "SubscribeToTask"
9
+
10
+ attr_reader :id, :tenant
11
+
12
+ def initialize(id:, tenant: nil)
13
+ @id = id
14
+ @tenant = tenant
15
+ end
16
+
17
+ def execute_json_rpc(protocol, &block)
18
+ protocol.stream(METHOD, params) do |response|
19
+ sub = Streaming::Subscription.new(response)
20
+ return sub unless block
21
+
22
+ sub.each(&block)
23
+ end
24
+ end
25
+
26
+ def execute_http_json(protocol, &block)
27
+ protocol.stream("/tasks/#{id}:subscribe", method: :get) do |response|
28
+ sub = Streaming::Subscription.new(response)
29
+ return sub unless block
30
+
31
+ sub.each(&block)
32
+ end
33
+ end
34
+
35
+ def params
36
+ { "id" => id, "tenant" => tenant }.compact
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "operation/executable"
4
+ require_relative "operation/send_message"
5
+ require_relative "operation/send_streaming_message"
6
+ require_relative "operation/get_task"
7
+ require_relative "operation/list_tasks"
8
+ require_relative "operation/cancel_task"
9
+ require_relative "operation/subscribe_to_task"
10
+ require_relative "operation/create_task_push_notification_config"
11
+ require_relative "operation/get_task_push_notification_config"
12
+ require_relative "operation/list_task_push_notification_configs"
13
+ require_relative "operation/delete_task_push_notification_config"
14
+ require_relative "operation/get_extended_agent_card"
15
+
16
+ module A2A
17
+ module Operation
18
+ end
19
+ end