@@ -24,7 +24,7 @@ import kotlinx.serialization.json.JsonElement
2424
2525/* * A2A protocol helpers for A2UI integration. */
2626object A2uiA2a {
27- const val A2UI_EXTENSION_URI = " https://a2ui.org/a2a-extension/a2ui/v0.8 "
27+ const val A2UI_EXTENSION_BASE_URI = " https://a2ui.org/a2a-extension/a2ui/v "
2828 const val MIME_TYPE_KEY = " mimeType"
2929 const val A2UI_MIME_TYPE = " application/json+a2ui"
3030
@@ -42,12 +42,13 @@ object A2uiA2a {
4242
4343 /* * Creates the A2UI AgentExtension configuration. */
4444 fun getA2uiAgentExtension (
45+ version : String ,
4546 acceptsInlineCatalogs : Boolean = false,
4647 supportedCatalogIds : List <String > = emptyList(),
4748 ): AgentExtension {
4849 val params = mutableMapOf<String , Any >()
4950 if (acceptsInlineCatalogs) {
50- params[A2uiConstants .INLINE_CATALOGS_KEY ] = true
51+ params[A2uiConstants .ACCEPTS_INLINE_CATALOGS_KEY ] = true
5152 }
5253 if (supportedCatalogIds.isNotEmpty()) {
5354 params[A2uiConstants .SUPPORTED_CATALOG_IDS_KEY ] = supportedCatalogIds
@@ -56,27 +57,70 @@ object A2uiA2a {
5657 val isSupportRequired = false
5758 return AgentExtension (
5859 " Provides agent driven UI using the A2UI JSON format." ,
59- params,
60+ if (params.isEmpty()) null else params,
6061 isSupportRequired,
61- A2UI_EXTENSION_URI ,
62+ " $A2UI_EXTENSION_BASE_URI$version " ,
6263 )
6364 }
6465
66+ /* *
67+ * Selects the newest A2UI extension URI from the matched extensions.
68+ *
69+ * @param requestedExtensions List of extension URIs requested by the client.
70+ * @param advertisedExtensions List of extension URIs advertised by the agent.
71+ * @return The newest overlapping A2UI extension URI, or null if none match.
72+ */
73+ fun selectNewestA2uiExtension (
74+ requestedExtensions : List <String >,
75+ advertisedExtensions : List <String >,
76+ ): String? {
77+ val baseUri = A2UI_EXTENSION_BASE_URI
78+ val matched =
79+ requestedExtensions.intersect(advertisedExtensions.toSet()).filter { it.startsWith(baseUri) }
80+
81+ if (matched.isEmpty()) return null
82+
83+ return matched.maxWithOrNull(
84+ Comparator { uri1, uri2 ->
85+ val v1 = uri1.removePrefix(baseUri)
86+ val v2 = uri2.removePrefix(baseUri)
87+ compareVersions(v1, v2)
88+ }
89+ )
90+ }
91+
92+ private fun compareVersions (v1 : String , v2 : String ): Int {
93+ val parts1 = v1.split(' .' ).map { it.toIntOrNull() ? : 0 }
94+ val parts2 = v2.split(' .' ).map { it.toIntOrNull() ? : 0 }
95+ val length = maxOf(parts1.size, parts2.size)
96+ for (i in 0 until length) {
97+ val p1 = parts1.getOrElse(i) { 0 }
98+ val p2 = parts2.getOrElse(i) { 0 }
99+ if (p1 != p2) {
100+ return p1.compareTo(p2)
101+ }
102+ }
103+ return 0
104+ }
105+
65106 /* *
66107 * Activates the A2UI extension if requested in the context.
67108 *
68109 * @param requestedExtensions List of extension URIs requested by the client.
110+ * @param advertisedExtensions List of extension URIs advertised by the agent.
69111 * @param addActivatedExtension Callback to register an activated extension.
70- * @return True if A2UI was activated, false otherwise .
112+ * @return The version string of the activated A2UI extension, or null if not activated .
71113 */
72114 fun tryActivateA2uiExtension (
73115 requestedExtensions : List <String >,
116+ advertisedExtensions : List <String >,
74117 addActivatedExtension : (String ) -> Unit ,
75- ): Boolean {
76- if (A2UI_EXTENSION_URI in requestedExtensions) {
77- addActivatedExtension(A2UI_EXTENSION_URI )
78- return true
118+ ): String? {
119+ val selectedUri = selectNewestA2uiExtension(requestedExtensions, advertisedExtensions)
120+ if (selectedUri != null ) {
121+ addActivatedExtension(selectedUri)
122+ return selectedUri.removePrefix(A2UI_EXTENSION_BASE_URI )
79123 }
80- return false
124+ return null
81125 }
82126}
0 commit comments