Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions a2a/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@

plugins {
kotlin("multiplatform")
id("com.android.kotlin.multiplatform.library")
id("maven-publish")
}

kotlin {
// AGP 9 KMP Android library target (replaces com.android.library + androidTarget).
android {
namespace = "com.google.adk.a2a"
compileSdk = rootProject.extra["androidCompileSdk"] as Int
// A2A requires API 35: the SDK's ClientBuilder.build() calls ServiceLoader.stream() (API 35+).
// Core stays at the shared androidMinSdk; only A2A gates higher.
minSdk = 35
}
jvm()

sourceSets {
Expand All @@ -38,14 +47,22 @@ kotlin {
val commonJvmAndroidMain by creating {
dependsOn(commonMain)
dependencies {
implementation(libs.jackson.databind)
implementation(libs.jackson.datatype.jsr310)
implementation(libs.kotlinx.serialization)
implementation(libs.a2a.sdk.client)
implementation(libs.a2a.sdk.common)
implementation(libs.a2a.sdk.spec)
}
}
// jvmMain: deprecated v0.3 (`io.a2a.*`) path, JVM-only.
// jvmMain hosts the deprecated v0.3 path (JVM-only); androidMain stays v1.0-only.
val jvmMain by getting {
dependsOn(commonJvmAndroidMain)
dependencies {
// JVM v1.0 factory uses the SDK's proto-based JSON-RPC transport; kept off the Android
// path.
implementation(libs.a2a.sdk.transport.jsonrpc)
// Jackson is JVM-only (deprecated v0.3 converters); kept off the Android artifact.
implementation(libs.jackson.databind)
implementation(libs.jackson.datatype.jsr310)
implementation(libs.jackson.module.kotlin)
implementation(libs.a2a.legacy.sdk.client)
implementation(libs.a2a.legacy.sdk.common)
Expand All @@ -58,19 +75,26 @@ kotlin {
implementation(libs.google.truth)
implementation(libs.mockito.kotlin)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.okhttp.mockwebserver)
implementation(libs.a2a.legacy.sdk.client)
implementation(libs.a2a.legacy.sdk.spec)
}
}
val androidMain by getting {
dependsOn(commonJvmAndroidMain)
dependencies { implementation(libs.a2a.sdk.http.client.android) }
}
}
}

// Coordinates the Kotlin Multiplatform plugin uses for the publications it
// auto-creates:
// - `kotlinMultiplatform` -> google-adk-kotlin-a2a (root metadata)
// - `jvm` -> google-adk-kotlin-a2a-jvm (KMP target)
// POM metadata, Dokka javadoc, and GPG signing are configured in the root
// build.gradle.kts.
// - `kotlinMultiplatform` -> google-adk-kotlin-a2a (root metadata)
// - `jvm` -> google-adk-kotlin-a2a-jvm (KMP target)
// - `androidRelease` -> google-adk-kotlin-a2a-android (KMP target)
// Per-target suffixes (`-jvm`, `-android`) are appended by the KMP plugin
// automatically. POM metadata, Dokka javadoc, and GPG signing are configured in
// the root build.gradle.kts.
publishing {
publications.withType<MavenPublication>().configureEach {
if (name == "kotlinMultiplatform") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.adk.kt.a2a.android

import com.google.adk.kt.a2a.agent.A2AAgentImpl
import com.google.adk.kt.a2a.agent.BaseRemoteA2AAgent
import com.google.adk.kt.a2a.agent.resolveAgentCard
import com.google.adk.kt.agents.BaseAgent
import com.google.adk.kt.callbacks.AfterAgentCallback
import com.google.adk.kt.callbacks.BeforeAgentCallback
import org.a2aproject.sdk.client.Client
import org.a2aproject.sdk.client.config.ClientConfig
import org.a2aproject.sdk.client.http.A2AHttpClient
import org.a2aproject.sdk.client.http.AndroidA2AHttpClient
import org.a2aproject.sdk.spec.AgentCard

/**
* Builds a framework-internal Android A2A [Client] backed by the proto-free, non-streaming
* [JsonRpcHttpClientTransport] (which uses [httpClient], an [AndroidA2AHttpClient] by default).
*/
internal fun androidA2AClient(
agentCard: AgentCard,
httpClient: A2AHttpClient = AndroidA2AHttpClient(),
): Client =
Client.builder(agentCard)
.clientConfig(ClientConfig.Builder().setStreaming(false).build())
.withTransport(
JsonRpcHttpClientTransport::class.java,
JsonRpcHttpClientTransportConfig(httpClient),
)
.build()

/**
* Builds an Android [A2AAgent] from an already-resolved [agentCard], wiring up the Android client
* so the caller never supplies a client and card separately.
*
* The Android proto-free transport supports only non-streaming `message/send`, so the agent always
* runs in non-streaming mode regardless of the remote card's streaming capability.
*/
fun AndroidA2AAgent(
name: String,
agentCard: AgentCard,
httpClient: A2AHttpClient = AndroidA2AHttpClient(),
subAgents: List<BaseAgent> = emptyList(),
beforeAgentCallbacks: List<BeforeAgentCallback> = emptyList(),
afterAgentCallbacks: List<AfterAgentCallback> = emptyList(),
): BaseRemoteA2AAgent =
A2AAgentImpl(
name = name,
a2aClient = androidA2AClient(agentCard, httpClient),
agentCard = agentCard,
streaming = false,
subAgents = subAgents,
beforeAgentCallbacks = beforeAgentCallbacks,
afterAgentCallbacks = afterAgentCallbacks,
)

/**
* Builds an Android [A2AAgent] from [agentCardUrl], auto-fetching the [AgentCard] from the remote
* agent's `/.well-known/agent-card.json` (like ADK Python/Go). Suspends on the network fetch, so
* call it off the main thread.
*/
suspend fun AndroidA2AAgent(
name: String,
agentCardUrl: String,
httpClient: A2AHttpClient = AndroidA2AHttpClient(),
subAgents: List<BaseAgent> = emptyList(),
beforeAgentCallbacks: List<BeforeAgentCallback> = emptyList(),
afterAgentCallbacks: List<AfterAgentCallback> = emptyList(),
): BaseRemoteA2AAgent =
AndroidA2AAgent(
name = name,
agentCard = resolveAgentCard(httpClient, agentCardUrl),
httpClient = httpClient,
subAgents = subAgents,
beforeAgentCallbacks = beforeAgentCallbacks,
afterAgentCallbacks = afterAgentCallbacks,
)
Loading
Loading