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
18 changes: 9 additions & 9 deletions client/base/src/main/java/io/a2a/client/AbstractClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -384,23 +384,23 @@ public abstract void resubscribe(@NonNull TaskIdParams request,
@Nullable ClientCallContext context) throws A2AClientException;

/**
* Retrieve the AgentCard.
* Retrieve the extended AgentCard.
*
* @return the AgentCard
* @throws A2AClientException if retrieving the agent card fails for any reason
* @return the extended AgentCard
* @throws A2AClientException if retrieving the extended agent card fails for any reason
*/
public AgentCard getAgentCard() throws A2AClientException {
return getAgentCard(null);
public AgentCard getExtendedAgentCard() throws A2AClientException {
return getExtendedAgentCard(null);
}

/**
* Retrieve the AgentCard.
* Retrieve the extended AgentCard.
*
* @param context optional client call context for the request (may be {@code null})
* @return the AgentCard
* @throws A2AClientException if retrieving the agent card fails for any reason
* @return the extended AgentCard
* @throws A2AClientException if retrieving the extended agent card fails for any reason
*/
public abstract AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException;
public abstract AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException;

/**
* Close the transport and release any associated resources.
Expand Down
15 changes: 8 additions & 7 deletions client/base/src/main/java/io/a2a/client/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -592,27 +592,28 @@ public void resubscribe(@NonNull TaskIdParams request,
}

/**
* Retrieve the agent's current agent card.
* Retrieve the agent's extended agent card.
* <p>
* This method fetches the latest agent card from the agent. The card may have changed since
* This method fetches the extended agent card from the agent (if the extendedAgentCard capability is supported).
* The card may have changed since
* client construction (e.g., new skills added, capabilities updated). The client's internal
* reference is updated to the newly retrieved card.
* <p>
* Example:
* <pre>{@code
* AgentCard updatedCard = client.getAgentCard(null);
* AgentCard updatedCard = client.getExtendedAgentCard(null);
* System.out.println("Agent version: " + updatedCard.version());
* System.out.println("Skills: " + updatedCard.skills().size());
* }</pre>
*
* @param context custom call context for request interceptors (optional)
* @return the agent's current agent card
* @throws A2AClientException if the agent card cannot be retrieved
* @return the agent's extended agent card
* @throws A2AClientException if the extended agent card cannot be retrieved
* @see AgentCard
*/
@Override
public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
agentCard = clientTransport.getAgentCard(context);
public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
agentCard = clientTransport.getExtendedAgentCard(context);
return agentCard;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public void setUp() {
.description("Test skill")
.tags(Collections.singletonList("test"))
.build()))
.protocolVersion(CURRENT_PROTOCOL_VERSION)
.protocolVersions(CURRENT_PROTOCOL_VERSION)
.supportedInterfaces(java.util.Arrays.asList(
new AgentInterface(TransportProtocol.JSONRPC.asString(), AGENT_URL),
new AgentInterface(TransportProtocol.HTTP_JSON.asString(), AGENT_URL),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class ClientBuilderTest {
.tags(Collections.singletonList("hello world"))
.examples(List.of("hi", "hello world"))
.build()))
.protocolVersion(CURRENT_PROTOCOL_VERSION)
.protocolVersions(CURRENT_PROTOCOL_VERSION)
.supportedInterfaces(List.of(
new AgentInterface(TransportProtocol.JSONRPC.asString(), "http://localhost:9999")))
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public EventStreamObserver(Consumer<StreamingEventKind> eventHandler, Consumer<T
public void onNext(StreamResponse response) {
StreamingEventKind event;
switch (response.getPayloadCase()) {
case MSG:
event = FromProto.message(response.getMsg());
case MESSAGE:
event = FromProto.message(response.getMessage());
break;
case TASK:
event = FromProto.task(response.getTask());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import io.a2a.common.A2AErrorMessages;
import io.a2a.spec.A2AClientException;
import io.a2a.spec.ContentTypeNotSupportedError;
import io.a2a.spec.ExtendedCardNotConfiguredError;
import io.a2a.spec.ExtendedAgentCardNotConfiguredError;
import io.a2a.spec.ExtensionSupportRequiredError;
import io.a2a.spec.InvalidAgentResponseError;
import io.a2a.spec.InvalidParamsError;
Expand Down Expand Up @@ -56,7 +56,7 @@ public static A2AClientException mapGrpcError(Throwable e, String errorPrefix) {
} else if (description.contains("InvalidAgentResponseError")) {
return new A2AClientException(errorPrefix + description, new InvalidAgentResponseError(null, description, null));
} else if (description.contains("ExtendedCardNotConfiguredError")) {
return new A2AClientException(errorPrefix + description, new ExtendedCardNotConfiguredError(null, description, null));
return new A2AClientException(errorPrefix + description, new ExtendedAgentCardNotConfiguredError(null, description, null));
} else if (description.contains("ExtensionSupportRequiredError")) {
return new A2AClientException(errorPrefix + description, new ExtensionSupportRequiredError(null, description, null));
} else if (description.contains("VersionNotSupportedError")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.a2a.spec.A2AMethods.CANCEL_TASK_METHOD;
import static io.a2a.spec.A2AMethods.DELETE_TASK_PUSH_NOTIFICATION_CONFIG_METHOD;
import static io.a2a.spec.A2AMethods.GET_EXTENDED_AGENT_CARD_METHOD;
import static io.a2a.spec.A2AMethods.GET_TASK_METHOD;
import static io.a2a.spec.A2AMethods.GET_TASK_PUSH_NOTIFICATION_CONFIG_METHOD;
import static io.a2a.spec.A2AMethods.LIST_TASK_METHOD;
Expand All @@ -26,6 +27,7 @@
import io.a2a.grpc.A2AServiceGrpc;
import io.a2a.grpc.A2AServiceGrpc.A2AServiceBlockingV2Stub;
import io.a2a.grpc.A2AServiceGrpc.A2AServiceStub;
import io.a2a.grpc.GetExtendedAgentCardRequest;
import io.a2a.grpc.utils.ProtoUtils.FromProto;
import io.a2a.grpc.utils.ProtoUtils.ToProto;
import io.a2a.jsonrpc.common.wrappers.ListTasksResult;
Expand Down Expand Up @@ -65,7 +67,7 @@ public class GrpcTransport implements ClientTransport {
private final A2AServiceBlockingV2Stub blockingStub;
private final A2AServiceStub asyncStub;
private final @Nullable List<ClientCallInterceptor> interceptors;
private AgentCard agentCard;
private final AgentCard agentCard;
private final String agentTenant;

public GrpcTransport(Channel channel, AgentCard agentCard) {
Expand Down Expand Up @@ -104,8 +106,8 @@ public EventKind sendMessage(MessageSendParams request, @Nullable ClientCallCont
try {
A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders);
io.a2a.grpc.SendMessageResponse response = stubWithMetadata.sendMessage(sendMessageRequest);
if (response.hasMsg()) {
return FromProto.message(response.getMsg());
if (response.hasMessage()) {
return FromProto.message(response.getMessage());
} else if (response.hasTask()) {
return FromProto.task(response.getTask());
} else {
Expand Down Expand Up @@ -195,8 +197,12 @@ public ListTasksResult listTasks(ListTasksParams request, @Nullable ClientCallCo
if (request.historyLength() != null) {
requestBuilder.setHistoryLength(request.historyLength());
}
if (request.lastUpdatedAfter() != null) {
requestBuilder.setLastUpdatedAfter(request.lastUpdatedAfter().toEpochMilli());
if (request.statusTimestampAfter() != null) {
requestBuilder.setStatusTimestampAfter(
com.google.protobuf.Timestamp.newBuilder()
.setSeconds(request.statusTimestampAfter().getEpochSecond())
.setNanos(request.statusTimestampAfter().getNano())
.build());
}
if (request.includeArtifacts() != null) {
requestBuilder.setIncludeArtifacts(request.includeArtifacts());
Expand Down Expand Up @@ -344,9 +350,19 @@ private MessageSendParams createRequestWithTenant(MessageSendParams request) {
}

@Override
public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
// TODO: Determine how to handle retrieving the authenticated extended agent card
return agentCard;
public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
GetExtendedAgentCardRequest request = GetExtendedAgentCardRequest.newBuilder()
.build();
PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD, request, agentCard, context);

try {
A2AServiceBlockingV2Stub stubWithMetadata = createBlockingStubWithMetadata(context, payloadAndHeaders);
io.a2a.grpc.AgentCard response = stubWithMetadata.getExtendedAgentCard(request);

return FromProto.agentCard(response);
} catch (StatusRuntimeException | StatusException e) {
throw GrpcErrorMapper.mapGrpcError(e, "Failed to get extended agent card: ");
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,9 @@

import io.a2a.spec.A2AClientException;
import io.a2a.spec.ContentTypeNotSupportedError;
import io.a2a.spec.ExtendedCardNotConfiguredError;
import io.a2a.spec.ExtendedAgentCardNotConfiguredError;
import io.a2a.spec.ExtensionSupportRequiredError;
import io.a2a.spec.InvalidAgentResponseError;
import io.a2a.spec.InvalidParamsError;
import io.a2a.spec.InvalidRequestError;
import io.a2a.spec.JSONParseError;
import io.a2a.spec.MethodNotFoundError;
import io.a2a.spec.PushNotificationNotSupportedError;
import io.a2a.spec.TaskNotCancelableError;
import io.a2a.spec.TaskNotFoundError;
import io.a2a.spec.UnsupportedOperationError;
import io.a2a.spec.VersionNotSupportedError;
Expand Down Expand Up @@ -85,9 +79,9 @@ public void testExtendedCardNotConfiguredErrorUnmarshalling() {
// Verify the result
assertNotNull(result);
assertNotNull(result.getCause());
assertInstanceOf(ExtendedCardNotConfiguredError.class, result.getCause());
assertInstanceOf(ExtendedAgentCardNotConfiguredError.class, result.getCause());

ExtendedCardNotConfiguredError extendedCardError = (ExtendedCardNotConfiguredError) result.getCause();
ExtendedAgentCardNotConfiguredError extendedCardError = (ExtendedAgentCardNotConfiguredError) result.getCause();
assertNotNull(extendedCardError.getMessage());
assertTrue(extendedCardError.getMessage().contains("Extended card not configured"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.function.Consumer;

import com.google.protobuf.MessageOrBuilder;
import io.a2a.client.http.A2ACardResolver;

import io.a2a.client.http.A2AHttpClient;
import io.a2a.client.http.A2AHttpClientFactory;
import io.a2a.client.http.A2AHttpResponse;
Expand All @@ -33,12 +33,10 @@
import io.a2a.grpc.utils.JSONRPCUtils;
import io.a2a.grpc.utils.ProtoUtils;
import io.a2a.jsonrpc.common.json.JsonProcessingException;
import io.a2a.jsonrpc.common.wrappers.A2AMessage;
import io.a2a.jsonrpc.common.wrappers.A2AResponse;
import io.a2a.jsonrpc.common.wrappers.CancelTaskResponse;
import io.a2a.jsonrpc.common.wrappers.DeleteTaskPushNotificationConfigResponse;
import io.a2a.jsonrpc.common.wrappers.GetAuthenticatedExtendedCardRequest;
import io.a2a.jsonrpc.common.wrappers.GetAuthenticatedExtendedCardResponse;
import io.a2a.jsonrpc.common.wrappers.GetExtendedAgentCardResponse;
import io.a2a.jsonrpc.common.wrappers.GetTaskPushNotificationConfigResponse;
import io.a2a.jsonrpc.common.wrappers.GetTaskResponse;
import io.a2a.jsonrpc.common.wrappers.ListTaskPushNotificationConfigResponse;
Expand Down Expand Up @@ -71,8 +69,7 @@ public class JSONRPCTransport implements ClientTransport {
private final A2AHttpClient httpClient;
private final AgentInterface agentInterface;
private final @Nullable List<ClientCallInterceptor> interceptors;
private @Nullable AgentCard agentCard;
private boolean needsExtendedCard = false;
private final @Nullable AgentCard agentCard;

public JSONRPCTransport(String agentUrl) {
this(null, null, new AgentInterface("JSONRPC", agentUrl), null);
Expand All @@ -88,7 +85,6 @@ public JSONRPCTransport(@Nullable A2AHttpClient httpClient, @Nullable AgentCard
this.agentCard = agentCard;
this.agentInterface = agentInterface;
this.interceptors = interceptors;
this.needsExtendedCard = agentCard == null || agentCard.supportsExtendedAgentCard();
}

@Override
Expand Down Expand Up @@ -288,31 +284,15 @@ public void resubscribe(TaskIdParams request, Consumer<StreamingEventKind> event
}

@Override
public AgentCard getAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
A2ACardResolver resolver;
public AgentCard getExtendedAgentCard(@Nullable ClientCallContext context) throws A2AClientException {
try {
if (agentCard == null) {
resolver = new A2ACardResolver(httpClient, agentInterface.url(), agentInterface.tenant(), null, getHttpHeaders(context));
agentCard = resolver.getAgentCard();
needsExtendedCard = agentCard.supportsExtendedAgentCard();
}
if (!needsExtendedCard) {
return agentCard;
}

GetAuthenticatedExtendedCardRequest getExtendedAgentCardRequest = GetAuthenticatedExtendedCardRequest.builder()
.jsonrpc(A2AMessage.JSONRPC_VERSION)
.build(); // id will be randomly generated

PayloadAndHeaders payloadAndHeaders = applyInterceptors(GET_EXTENDED_AGENT_CARD_METHOD,
ProtoUtils.ToProto.extendedAgentCard(), agentCard, context);

try {
String httpResponseBody = sendPostRequest(Utils.buildBaseUrl(agentInterface, ""), payloadAndHeaders, GET_EXTENDED_AGENT_CARD_METHOD);
GetAuthenticatedExtendedCardResponse response = unmarshalResponse(httpResponseBody, GET_EXTENDED_AGENT_CARD_METHOD);
agentCard = response.getResult();
needsExtendedCard = false;
return agentCard;
GetExtendedAgentCardResponse response = unmarshalResponse(httpResponseBody, GET_EXTENDED_AGENT_CARD_METHOD);
return response.getResult();
} catch (IOException | InterruptedException | JsonProcessingException e) {
throw new A2AClientException("Failed to get authenticated extended agent card: " + e, e);
}
Expand Down
Loading