agent2agent 1.1.1 → 2.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.
- checksums.yaml +4 -4
- data/lib/a2a/agent.rb +98 -241
- data/lib/a2a/{faraday → client}/middleware/json_rpc/request.rb +10 -8
- data/lib/a2a/{faraday → client}/middleware/json_rpc/response.rb +10 -9
- data/lib/a2a/{faraday → client}/middleware/rest/request.rb +24 -18
- data/lib/a2a/{faraday → client}/middleware/rest/response.rb +12 -8
- data/lib/a2a/{faraday → client}/middleware/schema_request.rb +9 -8
- data/lib/a2a/client.rb +40 -39
- data/lib/a2a/errors/json_rpc_error.rb +1 -2
- data/lib/a2a/errors/rest_error.rb +1 -2
- data/lib/a2a/errors.rb +1 -2
- data/lib/a2a/protocol/json_schema/definition.rb +276 -0
- data/lib/a2a/protocol/json_schema/validation_error.rb +132 -0
- data/lib/a2a/{schema.rb → protocol/json_schema.rb} +211 -208
- data/lib/a2a/protocol/protobuf.rb +447 -0
- data/lib/a2a/protocol.rb +31 -0
- data/lib/a2a/server/bindings/grpc.rb +37 -0
- data/lib/a2a/server/bindings/json_rpc.rb +29 -9
- data/lib/a2a/server/bindings/rest.rb +26 -13
- data/lib/a2a/server/middleware/extract_message.rb +145 -0
- data/lib/a2a/{middleware → server/middleware}/fetch_task.rb +82 -89
- data/lib/a2a/{middleware → server/middleware}/limit_history_length.rb +40 -49
- data/lib/a2a/{middleware → server/middleware}/limit_pagination_size.rb +36 -42
- data/lib/a2a/server/middleware/sse_stream.rb +236 -0
- data/lib/a2a/{sse → server/sse}/event_parser.rb +70 -69
- data/lib/a2a/server/sse/json_rpc_stream.rb +89 -0
- data/lib/a2a/server/sse/rest_stream.rb +63 -0
- data/lib/a2a/server/sse/stream.rb +261 -0
- data/lib/a2a/server/triage.rb +35 -4
- data/lib/a2a/server/well_known.rb +27 -0
- data/lib/a2a/server.rb +44 -58
- data/lib/a2a/test_helpers.rb +34 -75
- data/lib/a2a/version.rb +1 -1
- data/lib/a2a.rb +7 -11
- data/lib/traces/provider/a2a/agent.rb +25 -0
- data/lib/traces/provider/a2a.rb +1 -1
- metadata +39 -37
- data/lib/a2a/middleware/extract_message.rb +0 -120
- data/lib/a2a/middleware/sse_stream.rb +0 -235
- data/lib/a2a/proto.rb +0 -444
- data/lib/a2a/schema/definition.rb +0 -148
- data/lib/a2a/schema/validation_error.rb +0 -129
- data/lib/a2a/server/dispatcher.rb +0 -127
- data/lib/a2a/sse/json_rpc_stream.rb +0 -88
- data/lib/a2a/sse/rest_stream.rb +0 -62
- data/lib/a2a/sse/stream.rb +0 -257
- data/lib/traces/provider/a2a/server/dispatcher.rb +0 -26
- /data/lib/a2a/{middleware.rb → server/middleware.rb} +0 -0
- /data/lib/a2a/{sse.rb → server/sse.rb} +0 -0
|
@@ -4,249 +4,252 @@ require "bundler/setup"
|
|
|
4
4
|
require "a2a"
|
|
5
5
|
|
|
6
6
|
module A2A
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
unless definitions.key?(name)
|
|
41
|
-
raise "No A2A definition found for #{name.inspect}!" \
|
|
42
|
-
"\nAvailable: #{list_definitions.join(", ")}"
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
encoded = URI::DEFAULT_PARSER.escape(name)
|
|
46
|
-
ref_schema = schemer.ref("#/definitions/#{encoded}")
|
|
47
|
-
build_definition_class(ref_schema, name, definitions[name])
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# All available definition titles, sorted.
|
|
52
|
-
#
|
|
53
|
-
# A2A::Schema.list_definitions
|
|
54
|
-
# #=> ["API Key Security Scheme", "Agent Capabilities", ...]
|
|
55
|
-
#
|
|
56
|
-
def list_definitions
|
|
57
|
-
raw_schema.fetch("definitions", {}).keys.sort
|
|
58
|
-
end
|
|
7
|
+
module Protocol
|
|
8
|
+
# Schema-validated A2A protocol objects, powered by json_schemer.
|
|
9
|
+
#
|
|
10
|
+
# Loads the bundled data/a2a.json schema, rewrites external $ref
|
|
11
|
+
# values to internal #/definitions/... pointers, and dynamically
|
|
12
|
+
# generates Definition subclasses for each type.
|
|
13
|
+
#
|
|
14
|
+
# A2A::Protocol::JsonSchema["Agent Capabilities"]
|
|
15
|
+
# #=> Class < Definition with .schema, .valid?, reader methods
|
|
16
|
+
#
|
|
17
|
+
# A2A::Protocol::JsonSchema["Agent Card"]
|
|
18
|
+
# #=> Class < Definition
|
|
19
|
+
#
|
|
20
|
+
# A2A::Protocol::JsonSchema.list_definitions
|
|
21
|
+
# #=> ["API Key Security Scheme", "Agent Capabilities", ...]
|
|
22
|
+
#
|
|
23
|
+
module JsonSchema
|
|
24
|
+
DATA_PATH = File.expand_path("../../../data/a2a.json", __dir__).freeze
|
|
25
|
+
|
|
26
|
+
@definition_classes = {}
|
|
27
|
+
@schemer = nil
|
|
28
|
+
@raw_schema = nil
|
|
29
|
+
@ref_map = nil
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# Look up a definition by title.
|
|
33
|
+
#
|
|
34
|
+
# A2A::Protocol::JsonSchema["Agent Capabilities"]
|
|
35
|
+
# #=> Class < Definition
|
|
36
|
+
#
|
|
37
|
+
def [](name)
|
|
38
|
+
@definition_classes[name] ||= begin
|
|
39
|
+
definitions = raw_schema.fetch("definitions", {})
|
|
59
40
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
end
|
|
41
|
+
unless definitions.key?(name)
|
|
42
|
+
raise "No A2A definition found for #{name.inspect}!" \
|
|
43
|
+
"\nAvailable: #{list_definitions.join(", ")}"
|
|
44
|
+
end
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
46
|
+
encoded = URI::DEFAULT_PARSER.escape(name)
|
|
47
|
+
ref_schema = schemer.ref("#/definitions/#{encoded}")
|
|
48
|
+
build_definition_class(ref_schema, name, definitions[name])
|
|
49
|
+
end
|
|
50
|
+
end
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
52
|
+
# All available definition titles, sorted.
|
|
53
|
+
#
|
|
54
|
+
# A2A::Protocol::JsonSchema.list_definitions
|
|
55
|
+
# #=> ["API Key Security Scheme", "Agent Capabilities", ...]
|
|
56
|
+
#
|
|
57
|
+
def list_definitions
|
|
58
|
+
raw_schema.fetch("definitions", {}).keys.sort
|
|
59
|
+
end
|
|
78
60
|
|
|
79
|
-
|
|
61
|
+
# The JSONSchemer instance for the full A2A schema bundle.
|
|
62
|
+
# Cached after first access.
|
|
63
|
+
def schemer
|
|
64
|
+
@schemer ||= JSONSchemer.schema(raw_schema)
|
|
65
|
+
end
|
|
80
66
|
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
schema = JSON.parse(File.read(DATA_PATH))
|
|
85
|
-
map = build_ref_map(schema)
|
|
86
|
-
rewrite_refs!(schema, map)
|
|
87
|
-
schema
|
|
67
|
+
# The parsed + ref-rewritten JSON schema hash.
|
|
68
|
+
def raw_schema
|
|
69
|
+
@raw_schema ||= load_and_rewrite_schema
|
|
88
70
|
end
|
|
89
71
|
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
72
|
+
# Reset all cached state (useful for tests).
|
|
73
|
+
def reset!
|
|
74
|
+
@definition_classes.clear
|
|
75
|
+
@schemer = nil
|
|
76
|
+
@raw_schema = nil
|
|
77
|
+
@ref_map = nil
|
|
78
|
+
end
|
|
94
79
|
|
|
95
|
-
|
|
80
|
+
private
|
|
96
81
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
82
|
+
# Load data/a2a.json, build the $ref rewrite map, and
|
|
83
|
+
# walk the entire tree replacing external refs with internal ones.
|
|
84
|
+
def load_and_rewrite_schema
|
|
85
|
+
schema = JSON.parse(File.read(DATA_PATH))
|
|
86
|
+
map = build_ref_map(schema)
|
|
87
|
+
rewrite_refs!(schema, map)
|
|
88
|
+
schema
|
|
101
89
|
end
|
|
102
90
|
|
|
103
|
-
|
|
91
|
+
# Build a map from external $ref strings to internal
|
|
92
|
+
# #/definitions/Title pointers.
|
|
93
|
+
def build_ref_map(schema)
|
|
94
|
+
return @ref_map if @ref_map
|
|
104
95
|
|
|
105
|
-
|
|
106
|
-
refs.each do |ref_str|
|
|
107
|
-
type_name = ref_str
|
|
108
|
-
.sub(/\.jsonschema\.json\z/, "")
|
|
109
|
-
.split(".")
|
|
110
|
-
.last
|
|
96
|
+
definitions = schema.fetch("definitions", {})
|
|
111
97
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
98
|
+
pascal_to_title = {}
|
|
99
|
+
definitions.each_key do |title|
|
|
100
|
+
pascal = title.gsub(/\s+/, "")
|
|
101
|
+
pascal_to_title[pascal] = title
|
|
115
102
|
end
|
|
116
|
-
end
|
|
117
103
|
|
|
118
|
-
|
|
119
|
-
|
|
104
|
+
refs = collect_refs(schema)
|
|
105
|
+
|
|
106
|
+
map = {}
|
|
107
|
+
refs.each do |ref_str|
|
|
108
|
+
type_name = ref_str
|
|
109
|
+
.sub(/\.jsonschema\.json\z/, "")
|
|
110
|
+
.split(".")
|
|
111
|
+
.last
|
|
120
112
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
when Hash
|
|
125
|
-
obj.each do |k, v|
|
|
126
|
-
if k == "$ref" && v.is_a?(String) && !v.start_with?("#")
|
|
127
|
-
refs << v
|
|
128
|
-
else
|
|
129
|
-
collect_refs(v, refs)
|
|
113
|
+
if (title = pascal_to_title[type_name])
|
|
114
|
+
encoded = URI::DEFAULT_PARSER.escape(title)
|
|
115
|
+
map[ref_str] = "#/definitions/#{encoded}"
|
|
130
116
|
end
|
|
131
117
|
end
|
|
132
|
-
|
|
133
|
-
|
|
118
|
+
|
|
119
|
+
@ref_map = map
|
|
134
120
|
end
|
|
135
|
-
refs
|
|
136
|
-
end
|
|
137
121
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
122
|
+
# Recursively collect all $ref string values from a JSON tree.
|
|
123
|
+
def collect_refs(obj, refs = Set.new)
|
|
124
|
+
case obj
|
|
125
|
+
when Hash
|
|
126
|
+
obj.each do |k, v|
|
|
127
|
+
if k == "$ref" && v.is_a?(String) && !v.start_with?("#")
|
|
128
|
+
refs << v
|
|
129
|
+
else
|
|
130
|
+
collect_refs(v, refs)
|
|
131
|
+
end
|
|
148
132
|
end
|
|
133
|
+
when Array
|
|
134
|
+
obj.each { |v| collect_refs(v, refs) }
|
|
149
135
|
end
|
|
150
|
-
|
|
151
|
-
obj.each { |v| rewrite_refs!(v, map) }
|
|
136
|
+
refs
|
|
152
137
|
end
|
|
153
|
-
end
|
|
154
138
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
@property_refs = prop_refs
|
|
170
|
-
|
|
171
|
-
class << self
|
|
172
|
-
def schema = @schema
|
|
173
|
-
def definition_name = @definition_name
|
|
174
|
-
def schema_properties = @schema_properties
|
|
175
|
-
def snake_to_camel_map = @snake_to_camel
|
|
176
|
-
def property_refs = @property_refs
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
reader_pairs.each do |snake_sym, camel_key|
|
|
180
|
-
define_method(snake_sym) { @data[camel_key] }
|
|
139
|
+
# Walk the schema tree and replace all external $ref values
|
|
140
|
+
# with their internal #/definitions/... equivalents.
|
|
141
|
+
def rewrite_refs!(obj, map)
|
|
142
|
+
case obj
|
|
143
|
+
when Hash
|
|
144
|
+
obj.each do |k, v|
|
|
145
|
+
if k == "$ref" && v.is_a?(String) && map.key?(v)
|
|
146
|
+
obj[k] = map[v]
|
|
147
|
+
else
|
|
148
|
+
rewrite_refs!(v, map)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
when Array
|
|
152
|
+
obj.each { |v| rewrite_refs!(v, map) }
|
|
181
153
|
end
|
|
182
154
|
end
|
|
183
|
-
end
|
|
184
155
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if title && definitions.dig(title, "properties")
|
|
208
|
-
refs[camel_key] = [:array, title]
|
|
156
|
+
# Build a Definition subclass for a specific A2A type.
|
|
157
|
+
def build_definition_class(schema_instance, definition_name, raw_definition)
|
|
158
|
+
properties = raw_definition.fetch("properties", {})
|
|
159
|
+
camel_keys = properties.keys
|
|
160
|
+
snake_to_camel = build_snake_to_camel(camel_keys)
|
|
161
|
+
prop_refs = build_property_refs(properties)
|
|
162
|
+
|
|
163
|
+
reader_pairs = camel_keys.map { |ck| [camel_to_snake(ck).to_sym, ck] }
|
|
164
|
+
|
|
165
|
+
Class.new(Definition) do
|
|
166
|
+
@schema = schema_instance
|
|
167
|
+
@definition_name = definition_name
|
|
168
|
+
@schema_properties = camel_keys
|
|
169
|
+
@snake_to_camel = snake_to_camel
|
|
170
|
+
@property_refs = prop_refs
|
|
171
|
+
|
|
172
|
+
class << self
|
|
173
|
+
def schema = @schema
|
|
174
|
+
def definition_name = @definition_name
|
|
175
|
+
def schema_properties = @schema_properties
|
|
176
|
+
def snake_to_camel_map = @snake_to_camel
|
|
177
|
+
def property_refs = @property_refs
|
|
209
178
|
end
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if title && definitions.dig(title, "properties")
|
|
214
|
-
refs[camel_key] = [:map, title]
|
|
179
|
+
|
|
180
|
+
reader_pairs.each do |snake_sym, camel_key|
|
|
181
|
+
define_method(snake_sym) { @data[camel_key] }
|
|
215
182
|
end
|
|
216
183
|
end
|
|
217
184
|
end
|
|
218
185
|
|
|
219
|
-
|
|
220
|
-
|
|
186
|
+
# Inspect each property's raw schema for $ref pointers and build
|
|
187
|
+
# a map of { camelKey => [:kind, "Definition Title"] } so that
|
|
188
|
+
# Definition#initialize can auto-wrap nested Hashes.
|
|
189
|
+
#
|
|
190
|
+
# Three patterns:
|
|
191
|
+
# :object — direct $ref (e.g. task → Task)
|
|
192
|
+
# :array — items.$ref (e.g. artifacts → [Artifact, ...])
|
|
193
|
+
# :map — additionalProperties.$ref (e.g. securitySchemes → {k => SecurityScheme})
|
|
194
|
+
def build_property_refs(properties)
|
|
195
|
+
definitions = raw_schema.fetch("definitions", {})
|
|
196
|
+
refs = {}
|
|
197
|
+
|
|
198
|
+
properties.each do |camel_key, prop_schema|
|
|
199
|
+
if (ref = prop_schema["$ref"])
|
|
200
|
+
# Direct $ref — singular nested object
|
|
201
|
+
title = ref_title_for(ref)
|
|
202
|
+
if title && definitions.dig(title, "properties")
|
|
203
|
+
refs[camel_key] = [:object, title]
|
|
204
|
+
end
|
|
205
|
+
elsif prop_schema["type"] == "array" && (ref = prop_schema.dig("items", "$ref"))
|
|
206
|
+
# Array with $ref items
|
|
207
|
+
title = ref_title_for(ref)
|
|
208
|
+
if title && definitions.dig(title, "properties")
|
|
209
|
+
refs[camel_key] = [:array, title]
|
|
210
|
+
end
|
|
211
|
+
elsif prop_schema["type"] == "object" && (ref = prop_schema.dig("additionalProperties", "$ref"))
|
|
212
|
+
# Map with $ref additionalProperties
|
|
213
|
+
title = ref_title_for(ref)
|
|
214
|
+
if title && definitions.dig(title, "properties")
|
|
215
|
+
refs[camel_key] = [:map, title]
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
221
219
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
def ref_title_for(ref)
|
|
225
|
-
return nil unless ref.start_with?("#/definitions/")
|
|
220
|
+
refs
|
|
221
|
+
end
|
|
226
222
|
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
# Extract the definition title from an internal $ref pointer.
|
|
224
|
+
# e.g. "#/definitions/Task%20Status" => "Task Status"
|
|
225
|
+
def ref_title_for(ref)
|
|
226
|
+
return nil unless ref.start_with?("#/definitions/")
|
|
229
227
|
|
|
230
|
-
|
|
231
|
-
map = {}
|
|
232
|
-
camel_keys.each do |camel|
|
|
233
|
-
snake = camel_to_snake(camel)
|
|
234
|
-
map[snake] = camel
|
|
235
|
-
map[camel] = camel
|
|
228
|
+
URI::DEFAULT_PARSER.unescape(ref.sub("#/definitions/", ""))
|
|
236
229
|
end
|
|
237
|
-
map
|
|
238
|
-
end
|
|
239
230
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
231
|
+
def build_snake_to_camel(camel_keys)
|
|
232
|
+
map = {}
|
|
233
|
+
camel_keys.each do |camel|
|
|
234
|
+
snake = camel_to_snake(camel)
|
|
235
|
+
map[snake] = camel
|
|
236
|
+
map[camel] = camel
|
|
237
|
+
end
|
|
238
|
+
map
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def camel_to_snake(str)
|
|
242
|
+
str.gsub(/([A-Z])/) { "_#{$1.downcase}" }
|
|
243
|
+
.delete_prefix("_")
|
|
244
|
+
end
|
|
245
|
+
end
|
|
244
246
|
end
|
|
245
247
|
end
|
|
246
248
|
end
|
|
247
249
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
+
__END__
|
|
251
|
+
describe "A2A::Protocol::JsonSchema" do
|
|
252
|
+
schema = A2A::Protocol::JsonSchema
|
|
250
253
|
|
|
251
254
|
it "loads the raw schema" do
|
|
252
255
|
schema.raw_schema.should.be.kind_of(Hash)
|
|
@@ -291,7 +294,7 @@ test do
|
|
|
291
294
|
it "returns a Class that subclasses Definition" do
|
|
292
295
|
klass = schema["Agent Capabilities"]
|
|
293
296
|
klass.should.be.kind_of(Class)
|
|
294
|
-
(klass < A2A::
|
|
297
|
+
(klass < A2A::Protocol::JsonSchema::Definition).should == true
|
|
295
298
|
end
|
|
296
299
|
|
|
297
300
|
it "caches definition classes" do
|
|
@@ -375,7 +378,7 @@ test do
|
|
|
375
378
|
|
|
376
379
|
it "valid! raises ValidationError for invalid data" do
|
|
377
380
|
caps = schema["Agent Capabilities"].new(streaming: "not_a_bool")
|
|
378
|
-
lambda { caps.valid! }.should.raise(A2A::
|
|
381
|
+
lambda { caps.valid! }.should.raise(A2A::Protocol::JsonSchema::ValidationError)
|
|
379
382
|
end
|
|
380
383
|
|
|
381
384
|
it "detects additionalProperties violations" do
|
|
@@ -471,7 +474,7 @@ test do
|
|
|
471
474
|
schema.list_definitions.each do |name|
|
|
472
475
|
klass = schema[name]
|
|
473
476
|
instance = klass.new
|
|
474
|
-
instance.is_a?(A2A::
|
|
477
|
+
instance.is_a?(A2A::Protocol::JsonSchema::Definition).should == true
|
|
475
478
|
end
|
|
476
479
|
end
|
|
477
480
|
|
|
@@ -486,11 +489,11 @@ test do
|
|
|
486
489
|
}
|
|
487
490
|
}
|
|
488
491
|
)
|
|
489
|
-
response.task.should.be.kind_of(A2A::
|
|
492
|
+
response.task.should.be.kind_of(A2A::Protocol::JsonSchema::Definition)
|
|
490
493
|
response.task.id.should == "task-123"
|
|
491
494
|
response.task.context_id.should == "ctx-456"
|
|
492
495
|
# Deeply nested: Task.status is also auto-wrapped
|
|
493
|
-
response.task.status.should.be.kind_of(A2A::
|
|
496
|
+
response.task.status.should.be.kind_of(A2A::Protocol::JsonSchema::Definition)
|
|
494
497
|
response.task.status.state.should == "TASK_STATE_SUBMITTED"
|
|
495
498
|
end
|
|
496
499
|
|
|
@@ -505,7 +508,7 @@ test do
|
|
|
505
508
|
)
|
|
506
509
|
task.history.should.be.kind_of(Array)
|
|
507
510
|
task.history.length.should == 2
|
|
508
|
-
task.history[0].should.be.kind_of(A2A::
|
|
511
|
+
task.history[0].should.be.kind_of(A2A::Protocol::JsonSchema::Definition)
|
|
509
512
|
task.history[0].role.should == "ROLE_USER"
|
|
510
513
|
task.history[1].role.should == "ROLE_AGENT"
|
|
511
514
|
end
|